copy node graph before rendering
Previous iterations would use mutexes to prevent changing of the graph mid-render, however several user actions would need to capture these mutexes causing the main thread to hang until the current render job (frame/range of samples) was complete. We now copy the nodes necessary as part of the "compile" process so that the main thread shouldn't need nearly as much blocking while caching occurs.
This commit is contained in:
@@ -28,6 +28,10 @@
|
|||||||
*
|
*
|
||||||
* This is an abstract function. Since different types of Block will provide their lengths in different ways, it's
|
* This is an abstract function. Since different types of Block will provide their lengths in different ways, it's
|
||||||
* necessary to subclass and override the length() function for a Block to be usable.
|
* necessary to subclass and override the length() function for a Block to be usable.
|
||||||
|
*
|
||||||
|
* When overriding Node::copy(), the derivative class should also call Block::CopyParameters() on the new Block instance
|
||||||
|
* which will copy the block's name, length, and media in point. It does not copy any node-specific parameters like any
|
||||||
|
* input values or connections as per standard with Node::copy().
|
||||||
*/
|
*/
|
||||||
class Block : public Node
|
class Block : public Node
|
||||||
{
|
{
|
||||||
|
|||||||
+2
-2
@@ -130,8 +130,8 @@ void NodeParam::DisconnectEdge(NodeEdgePtr edge)
|
|||||||
output->parent()->LockUserInput();
|
output->parent()->LockUserInput();
|
||||||
input->parent()->LockUserInput();
|
input->parent()->LockUserInput();
|
||||||
|
|
||||||
output->edges_.removeAll(edge);
|
output->edges_.removeOne(edge);
|
||||||
input->edges_.removeAll(edge);
|
input->edges_.removeOne(edge);
|
||||||
|
|
||||||
output->parent()->UnlockUserInput();
|
output->parent()->UnlockUserInput();
|
||||||
input->parent()->UnlockUserInput();
|
input->parent()->UnlockUserInput();
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ void AudioRenderBackend::SetParameters(const AudioRenderingParams ¶ms)
|
|||||||
void AudioRenderBackend::InvalidateCache(const rational &start_range, const rational &end_range)
|
void AudioRenderBackend::InvalidateCache(const rational &start_range, const rational &end_range)
|
||||||
{
|
{
|
||||||
rational start_range_adj = qMax(rational(0), start_range);
|
rational start_range_adj = qMax(rational(0), start_range);
|
||||||
rational end_range_adj = qMin(viewer_node()->Length(), end_range);
|
rational end_range_adj = qMin(SequenceLength(), end_range);
|
||||||
|
|
||||||
// Add the range to the list
|
// Add the range to the list
|
||||||
cache_queue_.append(TimeRange(start_range_adj, end_range_adj));
|
cache_queue_.append(TimeRange(start_range_adj, end_range_adj));
|
||||||
@@ -41,6 +41,7 @@ void AudioRenderBackend::InvalidateCache(const rational &start_range, const rati
|
|||||||
void AudioRenderBackend::ConnectViewer(ViewerOutput *node)
|
void AudioRenderBackend::ConnectViewer(ViewerOutput *node)
|
||||||
{
|
{
|
||||||
connect(node, SIGNAL(AudioChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&)));
|
connect(node, SIGNAL(AudioChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&)));
|
||||||
|
connect(node, SIGNAL(AudioGraphChanged()), this, SLOT(QueueRecompile()));
|
||||||
|
|
||||||
// FIXME: Hardcoded format
|
// FIXME: Hardcoded format
|
||||||
SetParameters(AudioRenderingParams(node->audio_params(), SAMPLE_FMT_FLT));
|
SetParameters(AudioRenderingParams(node->audio_params(), SAMPLE_FMT_FLT));
|
||||||
@@ -49,6 +50,7 @@ void AudioRenderBackend::ConnectViewer(ViewerOutput *node)
|
|||||||
void AudioRenderBackend::DisconnectViewer(ViewerOutput *node)
|
void AudioRenderBackend::DisconnectViewer(ViewerOutput *node)
|
||||||
{
|
{
|
||||||
disconnect(node, SIGNAL(AudioChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&)));
|
disconnect(node, SIGNAL(AudioChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&)));
|
||||||
|
disconnect(node, SIGNAL(AudioGraphChanged()), this, SLOT(QueueRecompile()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioRenderBackend::GenerateCacheIDInternal(QCryptographicHash &hash)
|
bool AudioRenderBackend::GenerateCacheIDInternal(QCryptographicHash &hash)
|
||||||
|
|||||||
@@ -80,23 +80,12 @@ bool OpenGLBackend::CompileInternal()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Traverse node graph compiling where necessary
|
// Traverse node graph compiling where necessary
|
||||||
bool ret = TraverseCompiling(viewer_node());
|
return TraverseCompiling(viewer_node());
|
||||||
|
|
||||||
if (ret) {
|
|
||||||
//qDebug() << "Compiled successfully!";
|
|
||||||
compiled_ = true;
|
|
||||||
} else {
|
|
||||||
qDebug() << "Compile failed:" << GetError();
|
|
||||||
Decompile();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLBackend::DecompileInternal()
|
void OpenGLBackend::DecompileInternal()
|
||||||
{
|
{
|
||||||
shader_cache_.Clear();
|
shader_cache_.Clear();
|
||||||
compiled_ = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLBackend::TraverseCompiling(Node *n)
|
bool OpenGLBackend::TraverseCompiling(Node *n)
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ RenderBackend::RenderBackend(QObject *parent) :
|
|||||||
compiled_(false),
|
compiled_(false),
|
||||||
caching_(false),
|
caching_(false),
|
||||||
started_(false),
|
started_(false),
|
||||||
viewer_node_(nullptr)
|
viewer_node_(nullptr),
|
||||||
|
copied_viewer_node_(nullptr),
|
||||||
|
recompile_queued_(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,10 +101,34 @@ bool RenderBackend::IsInitiated()
|
|||||||
|
|
||||||
bool RenderBackend::Compile()
|
bool RenderBackend::Compile()
|
||||||
{
|
{
|
||||||
if (compiled_) {
|
if (recompile_queued_) {
|
||||||
|
Decompile();
|
||||||
|
recompile_queued_ = false;
|
||||||
|
} else if (compiled_) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get dependencies of viewer node
|
||||||
|
QList<Node*> nodes;
|
||||||
|
nodes.append(viewer_node_);
|
||||||
|
nodes.append(viewer_node_->GetDependencies());
|
||||||
|
|
||||||
|
// Copy all dependencies into graph
|
||||||
|
foreach (Node* n, nodes) {
|
||||||
|
Node* copy = n->copy();
|
||||||
|
|
||||||
|
// Copy values (but not connections yet)
|
||||||
|
Node::CopyInputs(n, copy, false);
|
||||||
|
|
||||||
|
copied_graph_.AddNode(copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We know that the first node will be the viewer node since we appended that first in the copy
|
||||||
|
copied_viewer_node_ = static_cast<ViewerOutput*>(copied_graph_.nodes().first());
|
||||||
|
|
||||||
|
// Copy connections
|
||||||
|
Node::DuplicateConnectionsBetweenLists(nodes, copied_graph_.nodes());
|
||||||
|
|
||||||
compiled_ = CompileInternal();
|
compiled_ = CompileInternal();
|
||||||
|
|
||||||
if (!compiled_) {
|
if (!compiled_) {
|
||||||
@@ -119,6 +145,12 @@ void RenderBackend::Decompile()
|
|||||||
}
|
}
|
||||||
|
|
||||||
DecompileInternal();
|
DecompileInternal();
|
||||||
|
|
||||||
|
copied_graph_.Clear();
|
||||||
|
|
||||||
|
copied_viewer_node_ = nullptr;
|
||||||
|
|
||||||
|
compiled_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderBackend::RegenerateCacheID()
|
void RenderBackend::RegenerateCacheID()
|
||||||
@@ -141,6 +173,15 @@ void RenderBackend::RegenerateCacheID()
|
|||||||
CacheIDChangedEvent(cache_id_);
|
CacheIDChangedEvent(cache_id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rational RenderBackend::SequenceLength()
|
||||||
|
{
|
||||||
|
if (viewer_node_ == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return viewer_node_->Length();
|
||||||
|
}
|
||||||
|
|
||||||
void RenderBackend::SetError(const QString &error)
|
void RenderBackend::SetError(const QString &error)
|
||||||
{
|
{
|
||||||
error_ = error;
|
error_ = error;
|
||||||
@@ -158,7 +199,7 @@ void RenderBackend::DisconnectViewer(ViewerOutput *node)
|
|||||||
|
|
||||||
void RenderBackend::CacheNext()
|
void RenderBackend::CacheNext()
|
||||||
{
|
{
|
||||||
if (!Init() || cache_queue_.isEmpty() || viewer_node() == nullptr || caching_) {
|
if (!Init() || cache_queue_.isEmpty() || !ViewerIsConnected() || caching_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +232,12 @@ bool RenderBackend::GenerateData(const TimeRange &range)
|
|||||||
|
|
||||||
ViewerOutput *RenderBackend::viewer_node() const
|
ViewerOutput *RenderBackend::viewer_node() const
|
||||||
{
|
{
|
||||||
return viewer_node_;
|
return copied_viewer_node_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RenderBackend::ViewerIsConnected() const
|
||||||
|
{
|
||||||
|
return viewer_node_ != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
DecoderCache *RenderBackend::decoder_cache()
|
DecoderCache *RenderBackend::decoder_cache()
|
||||||
@@ -245,3 +291,8 @@ void RenderBackend::ThreadRequestedSibling(NodeDependency dep)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RenderBackend::QueueRecompile()
|
||||||
|
{
|
||||||
|
recompile_queued_ = true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <QLinkedList>
|
#include <QLinkedList>
|
||||||
|
|
||||||
#include "decodercache.h"
|
#include "decodercache.h"
|
||||||
|
#include "node/graph.h"
|
||||||
#include "node/output/viewer/viewer.h"
|
#include "node/output/viewer/viewer.h"
|
||||||
#include "renderworker.h"
|
#include "renderworker.h"
|
||||||
|
|
||||||
@@ -45,6 +46,8 @@ protected:
|
|||||||
|
|
||||||
virtual void DecompileInternal() = 0;
|
virtual void DecompileInternal() = 0;
|
||||||
|
|
||||||
|
rational SequenceLength();
|
||||||
|
|
||||||
const QVector<QThread*>& threads();
|
const QVector<QThread*>& threads();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,6 +79,8 @@ protected:
|
|||||||
|
|
||||||
ViewerOutput* viewer_node() const;
|
ViewerOutput* viewer_node() const;
|
||||||
|
|
||||||
|
bool ViewerIsConnected() const;
|
||||||
|
|
||||||
DecoderCache* decoder_cache();
|
DecoderCache* decoder_cache();
|
||||||
|
|
||||||
const QString& cache_id() const;
|
const QString& cache_id() const;
|
||||||
@@ -104,6 +109,11 @@ private:
|
|||||||
*/
|
*/
|
||||||
ViewerOutput* viewer_node_;
|
ViewerOutput* viewer_node_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Internal reference to the copied viewer node we made in the compilation process
|
||||||
|
*/
|
||||||
|
ViewerOutput* copied_viewer_node_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Error string that can be set in SetError() to handle failures
|
* @brief Error string that can be set in SetError() to handle failures
|
||||||
*/
|
*/
|
||||||
@@ -115,9 +125,15 @@ private:
|
|||||||
qint64 cache_time_;
|
qint64 cache_time_;
|
||||||
QString cache_id_;
|
QString cache_id_;
|
||||||
|
|
||||||
|
NodeGraph copied_graph_;
|
||||||
|
|
||||||
|
bool recompile_queued_;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void ThreadRequestedSibling(NodeDependency dep);
|
void ThreadRequestedSibling(NodeDependency dep);
|
||||||
|
|
||||||
|
void QueueRecompile();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // RENDERBACKEND_H
|
#endif // RENDERBACKEND_H
|
||||||
|
|||||||
@@ -44,12 +44,12 @@ void VideoRenderBackend::InvalidateCache(const rational &start_range, const rati
|
|||||||
|
|
||||||
// Adjust range to min/max values
|
// Adjust range to min/max values
|
||||||
rational start_range_adj = qMax(rational(0), start_range);
|
rational start_range_adj = qMax(rational(0), start_range);
|
||||||
rational end_range_adj = qMin(viewer_node()->Length(), end_range);
|
rational end_range_adj = qMin(SequenceLength(), end_range);
|
||||||
|
|
||||||
/*qDebug() << "Cache invalidated between"
|
qDebug() << "Cache invalidated between"
|
||||||
<< start_range_adj.toDouble()
|
<< start_range_adj.toDouble()
|
||||||
<< "and"
|
<< "and"
|
||||||
<< end_range_adj.toDouble();*/
|
<< end_range_adj.toDouble();
|
||||||
|
|
||||||
// Snap start_range to timebase
|
// Snap start_range to timebase
|
||||||
double start_range_dbl = start_range_adj.toDouble();
|
double start_range_dbl = start_range_adj.toDouble();
|
||||||
@@ -96,7 +96,7 @@ void VideoRenderBackend::InvalidateCache(const rational &start_range, const rati
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove frames after this time code if it's changed
|
// Remove frames after this time code if it's changed
|
||||||
frame_cache_.Truncate(viewer_node()->Length());
|
frame_cache_.Truncate(SequenceLength());
|
||||||
|
|
||||||
CacheNext();
|
CacheNext();
|
||||||
}
|
}
|
||||||
@@ -115,6 +115,7 @@ void VideoRenderBackend::CloseInternal()
|
|||||||
void VideoRenderBackend::ConnectViewer(ViewerOutput *node)
|
void VideoRenderBackend::ConnectViewer(ViewerOutput *node)
|
||||||
{
|
{
|
||||||
connect(node, SIGNAL(VideoChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&)));
|
connect(node, SIGNAL(VideoChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&)));
|
||||||
|
connect(node, SIGNAL(VideoGraphChanged()), this, SLOT(QueueRecompile()));
|
||||||
|
|
||||||
// FIXME: Hardcoded format, mode, and divider
|
// FIXME: Hardcoded format, mode, and divider
|
||||||
SetParameters(VideoRenderingParams(node->video_params(), olive::PIX_FMT_RGBA16F, olive::kOffline, 2));
|
SetParameters(VideoRenderingParams(node->video_params(), olive::PIX_FMT_RGBA16F, olive::kOffline, 2));
|
||||||
@@ -123,6 +124,7 @@ void VideoRenderBackend::ConnectViewer(ViewerOutput *node)
|
|||||||
void VideoRenderBackend::DisconnectViewer(ViewerOutput *node)
|
void VideoRenderBackend::DisconnectViewer(ViewerOutput *node)
|
||||||
{
|
{
|
||||||
disconnect(node, SIGNAL(VideoChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&)));
|
disconnect(node, SIGNAL(VideoChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&)));
|
||||||
|
disconnect(node, SIGNAL(VideoGraphChanged()), this, SLOT(QueueRecompile()));
|
||||||
}
|
}
|
||||||
|
|
||||||
const VideoRenderingParams &VideoRenderBackend::params() const
|
const VideoRenderingParams &VideoRenderBackend::params() const
|
||||||
|
|||||||
Reference in New Issue
Block a user