renderer: share render backend for auto-cache tasks
Since auto-cache events start and stop fairly frequently (and are somewhat heavy to create/destroy), we can save a lot of cycles over time by sharing the same backend between them all.
This commit is contained in:
@@ -537,6 +537,25 @@ bool Node::OutputsTo(const QString &id, bool recursively) const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Node::OutputsTo(NodeInput *input, bool recursively) const
|
||||
{
|
||||
QList<NodeOutput*> outputs = GetOutputs();
|
||||
|
||||
foreach (NodeOutput* output, outputs) {
|
||||
foreach (NodeEdgePtr edge, output->edges()) {
|
||||
NodeInput* connected = edge->input();
|
||||
|
||||
if (connected == input) {
|
||||
return true;
|
||||
} else if (recursively && connected->parentNode()->OutputsTo(input, recursively)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Node::InputsFrom(Node *n, bool recursively) const
|
||||
{
|
||||
QList<NodeInput*> inputs = GetInputsIncludingArrays();
|
||||
|
||||
@@ -220,6 +220,11 @@ public:
|
||||
*/
|
||||
bool OutputsTo(const QString& id, bool recursively) const;
|
||||
|
||||
/**
|
||||
* @brief Same as OutputsTo(Node*), but for a specific node input rather than just a node.
|
||||
*/
|
||||
bool OutputsTo(NodeInput* input, bool recursively) const;
|
||||
|
||||
/**
|
||||
* @brief Returns whether this node ever receives an input from a particular node instance
|
||||
*/
|
||||
|
||||
@@ -195,27 +195,37 @@ std::list<TimeRange> RenderBackend::SplitRangeIntoChunks(const TimeRange &r)
|
||||
|
||||
void RenderBackend::NodeGraphChanged(NodeInput *source)
|
||||
{
|
||||
if (!graph_update_queue_.isEmpty()) {
|
||||
// First, check if anything in our queue is a dependency of this input. If so, we should remove
|
||||
// it and just update this input.
|
||||
// We need to determine:
|
||||
// - If we don't have this input, assume that it's coming soon and ignore it
|
||||
// - If we do, is this input a child of another input we're already copying?
|
||||
// - Or are any of the queued inputs children of this one?
|
||||
|
||||
// First we need to find our copy of the input being queued
|
||||
Node* our_copy_node = copy_map_.value(source->parentNode());
|
||||
// First we need to find our copy of the input being queued
|
||||
Node* our_copy_node = copy_map_.value(source->parentNode());
|
||||
|
||||
if (our_copy_node) {
|
||||
NodeInput* our_copy = our_copy_node->GetInputWithID(source->id());
|
||||
QList<Node*> our_copy_deps = our_copy->GetDependencies(our_copy);
|
||||
// If we don't have this node yet, assume it's coming in a later copy in which case it'll be
|
||||
// copied then
|
||||
if (!our_copy_node) {
|
||||
// Assert that there are updates coming
|
||||
Q_ASSERT(!graph_update_queue_.isEmpty());
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i=0;i<graph_update_queue_.size();i++) {
|
||||
NodeInput* check_input = graph_update_queue_.at(i);
|
||||
Node* check_input_our_copy = copy_map_.value(check_input->parentNode());
|
||||
// If we're here, we must have this node. Determine if we're already copying a "parent" of this
|
||||
for (int i=0; i<graph_update_queue_.size(); i++) {
|
||||
NodeInput* queued_input = graph_update_queue_.at(i);
|
||||
|
||||
// If this input isn't connected anymore, it obviously won't come up as a dependency
|
||||
if (our_copy_deps.contains(check_input_our_copy)) {
|
||||
graph_update_queue_.removeAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
// Check if this dependency graph is already queued
|
||||
if (source->parentNode()->OutputsTo(queued_input, true)) {
|
||||
// In which case, no further copy is necessary
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this input supersedes an already queued input
|
||||
if (queued_input->parentNode()->OutputsTo(source, true)) {
|
||||
// In which case, we don't need to queue it and can queue our own
|
||||
graph_update_queue_.removeAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,15 +339,9 @@ void RenderBackend::RunNextJob()
|
||||
|
||||
void RenderBackend::ProcessUpdateQueue()
|
||||
{
|
||||
/*
|
||||
while (!graph_update_queue_.isEmpty()) {
|
||||
CopyNodeInputValue(graph_update_queue_.takeFirst());
|
||||
}
|
||||
*/
|
||||
|
||||
// FIXME: SLOW DEBUGGING CODE
|
||||
CopyNodeInputValue(viewer_node_->texture_input());
|
||||
CopyNodeInputValue(viewer_node_->samples_input());
|
||||
}
|
||||
|
||||
QByteArray RenderBackend::HashNode(const Node *n, const VideoParams ¶ms, const rational &time)
|
||||
|
||||
@@ -43,6 +43,11 @@ public:
|
||||
|
||||
void Close();
|
||||
|
||||
ViewerOutput* GetViewerNode() const
|
||||
{
|
||||
return viewer_node_;
|
||||
}
|
||||
|
||||
void SetViewerNode(ViewerOutput* viewer_node);
|
||||
|
||||
void SetUpdateWithGraph(bool e)
|
||||
@@ -81,6 +86,16 @@ public:
|
||||
*/
|
||||
RenderTicketPtr RenderAudio(const TimeRange& r);
|
||||
|
||||
const VideoParams& GetVideoParams() const
|
||||
{
|
||||
return video_params_;
|
||||
}
|
||||
|
||||
const AudioParams& GetAudioParams() const
|
||||
{
|
||||
return audio_params_;
|
||||
}
|
||||
|
||||
void SetVideoParams(const VideoParams& params);
|
||||
|
||||
void SetAudioParams(const AudioParams& params);
|
||||
|
||||
Vendored
+18
-6
@@ -27,16 +27,18 @@
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
CacheTask::CacheTask(RenderBackend *backend, bool in_out_only) :
|
||||
RenderTask(backend),
|
||||
in_out_only_(in_out_only)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
CacheTask::CacheTask(ViewerOutput* viewer, const VideoParams& vparams, const AudioParams &aparams, bool in_out_only) :
|
||||
RenderTask(viewer, vparams, aparams),
|
||||
in_out_only_(in_out_only)
|
||||
{
|
||||
SetTitle(tr("Caching \"%1\"").arg(viewer->media_name()));
|
||||
|
||||
backend()->EnablePreviewGeneration(job_time());
|
||||
|
||||
// Render fastest quality
|
||||
backend()->SetRenderMode(RenderMode::kOffline);
|
||||
Init();
|
||||
}
|
||||
|
||||
bool CacheTask::Run()
|
||||
@@ -83,4 +85,14 @@ void CacheTask::AudioDownloaded(const TimeRange &range, SampleBufferPtr samples)
|
||||
}
|
||||
}
|
||||
|
||||
void CacheTask::Init()
|
||||
{
|
||||
SetTitle(tr("Caching \"%1\"").arg(viewer()->media_name()));
|
||||
|
||||
backend()->EnablePreviewGeneration(job_time());
|
||||
|
||||
// Render fastest quality
|
||||
backend()->SetRenderMode(RenderMode::kOffline);
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
Vendored
+3
@@ -31,6 +31,7 @@ class CacheTask : public RenderTask
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CacheTask(RenderBackend* backend, bool in_out_only);
|
||||
CacheTask(ViewerOutput* viewer,
|
||||
const VideoParams &vparams,
|
||||
const AudioParams &aparams,
|
||||
@@ -46,6 +47,8 @@ protected:
|
||||
virtual void AudioDownloaded(const TimeRange& range, SampleBufferPtr samples) override;
|
||||
|
||||
private:
|
||||
void Init();
|
||||
|
||||
bool in_out_only_;
|
||||
|
||||
QThreadPool download_threads_;
|
||||
|
||||
+30
-17
@@ -24,18 +24,31 @@
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
RenderTask::RenderTask(ViewerOutput* viewer, const VideoParams &vparams, const AudioParams &aparams) :
|
||||
viewer_(viewer),
|
||||
video_params_(vparams),
|
||||
audio_params_(aparams)
|
||||
RenderTask::RenderTask(RenderBackend *backend) :
|
||||
backend_(backend)
|
||||
{
|
||||
job_time_ = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
// FIXME: This makes a full copy of the node graph every time it starts, there must be a better
|
||||
// way.
|
||||
backend_.SetViewerNode(viewer_);
|
||||
backend_.SetVideoParams(video_params_);
|
||||
backend_.SetAudioParams(audio_params_);
|
||||
backend_is_ours_ = false;
|
||||
}
|
||||
|
||||
RenderTask::RenderTask(ViewerOutput* viewer, const VideoParams &vparams, const AudioParams &aparams)
|
||||
{
|
||||
job_time_ = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
backend_ = new OpenGLBackend();
|
||||
backend_->SetViewerNode(viewer);
|
||||
backend_->SetVideoParams(vparams);
|
||||
backend_->SetAudioParams(aparams);
|
||||
|
||||
backend_is_ours_ = true;
|
||||
}
|
||||
|
||||
RenderTask::~RenderTask()
|
||||
{
|
||||
if (backend_is_ours_) {
|
||||
backend_->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
struct TimeHashFuturePair {
|
||||
@@ -68,11 +81,11 @@ void RenderTask::Render(const TimeRangeList& video_range,
|
||||
const QMatrix4x4& mat,
|
||||
bool use_disk_cache)
|
||||
{
|
||||
backend_.SetVideoDownloadMatrix(mat);
|
||||
backend_->SetVideoDownloadMatrix(mat);
|
||||
|
||||
double progress_counter = 0;
|
||||
double total_length = 0;
|
||||
double video_frame_sz = video_params_.time_base().toDouble();
|
||||
double video_frame_sz = video_params().time_base().toDouble();
|
||||
|
||||
std::list<TimeRange> audio_queue;
|
||||
std::list<RangeSampleFuturePair> audio_lookup_table;
|
||||
@@ -93,11 +106,11 @@ void RenderTask::Render(const TimeRangeList& video_range,
|
||||
if (!video_range.isEmpty()) {
|
||||
QList<QByteArray> existing_hashes;
|
||||
|
||||
times = viewer_->video_frame_cache()->GetFrameListFromTimeRange(video_range);
|
||||
times = viewer()->video_frame_cache()->GetFrameListFromTimeRange(video_range);
|
||||
|
||||
total_length += video_frame_sz * times.size();
|
||||
|
||||
QFuture<QVector<QByteArray> > hash_future = backend_.Hash(times);
|
||||
QFuture<QVector<QByteArray> > hash_future = backend_->Hash(times);
|
||||
hashes = hash_future.result();
|
||||
|
||||
for (int i=0;i<times.size();i++) {
|
||||
@@ -141,7 +154,7 @@ void RenderTask::Render(const TimeRangeList& video_range,
|
||||
|
||||
// If not, check if it's in the filesystem
|
||||
if (!hash_exists) {
|
||||
hash_exists = QFileInfo::exists(viewer_->video_frame_cache()->CachePathName(p.hash));
|
||||
hash_exists = QFileInfo::exists(viewer()->video_frame_cache()->CachePathName(p.hash));
|
||||
|
||||
// If so, add it to the list so we don't have to check the filesystem again later
|
||||
if (hash_exists) {
|
||||
@@ -159,7 +172,7 @@ void RenderTask::Render(const TimeRangeList& video_range,
|
||||
|
||||
// If no existing disk cache was found, queue it now
|
||||
if (!hash_exists) {
|
||||
render_lookup_table.push_back({p.hash, backend_.RenderFrame(p.time)});
|
||||
render_lookup_table.push_back({p.hash, backend_->RenderFrame(p.time)});
|
||||
running_hashes.push_back(p.hash);
|
||||
}
|
||||
}
|
||||
@@ -169,7 +182,7 @@ void RenderTask::Render(const TimeRangeList& video_range,
|
||||
}
|
||||
|
||||
if (!IsCancelled() && !audio_queue.empty()) {
|
||||
audio_lookup_table.push_back({audio_queue.front(), backend_.RenderAudio(audio_queue.front())});
|
||||
audio_lookup_table.push_back({audio_queue.front(), backend_->RenderAudio(audio_queue.front())});
|
||||
audio_queue.pop_front();
|
||||
}
|
||||
|
||||
@@ -233,7 +246,7 @@ void RenderTask::Render(const TimeRangeList& video_range,
|
||||
}
|
||||
|
||||
// `Close` will block until all jobs are done making a safe deletion
|
||||
backend_.Close();
|
||||
backend_->Close();
|
||||
}
|
||||
|
||||
void RenderTask::SetAnchorPoint(const rational &r)
|
||||
|
||||
+11
-12
@@ -32,8 +32,11 @@ OLIVE_NAMESPACE_ENTER
|
||||
class RenderTask : public Task
|
||||
{
|
||||
public:
|
||||
RenderTask(RenderBackend* backend);
|
||||
RenderTask(ViewerOutput* viewer, const VideoParams &vparams, const AudioParams &aparams);
|
||||
|
||||
virtual ~RenderTask() override;
|
||||
|
||||
protected:
|
||||
void Render(const TimeRangeList &video_range,
|
||||
const TimeRangeList &audio_range,
|
||||
@@ -48,17 +51,17 @@ protected:
|
||||
|
||||
ViewerOutput* viewer() const
|
||||
{
|
||||
return viewer_;
|
||||
return backend_->GetViewerNode();
|
||||
}
|
||||
|
||||
VideoParams video_params() const
|
||||
{
|
||||
return video_params_;
|
||||
return backend_->GetVideoParams();
|
||||
}
|
||||
|
||||
AudioParams audio_params() const
|
||||
{
|
||||
return audio_params_;
|
||||
return backend_->GetAudioParams();
|
||||
}
|
||||
|
||||
void SetAnchorPoint(const rational& r);
|
||||
@@ -68,21 +71,17 @@ protected:
|
||||
return job_time_;
|
||||
}
|
||||
|
||||
OpenGLBackend* backend()
|
||||
RenderBackend* backend()
|
||||
{
|
||||
return &backend_;
|
||||
return backend_;
|
||||
}
|
||||
|
||||
private:
|
||||
ViewerOutput* viewer_;
|
||||
|
||||
VideoParams video_params_;
|
||||
|
||||
AudioParams audio_params_;
|
||||
|
||||
rational anchor_point_;
|
||||
|
||||
OpenGLBackend backend_;
|
||||
RenderBackend* backend_;
|
||||
|
||||
bool backend_is_ours_;
|
||||
|
||||
qint64 job_time_;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user