diff --git a/app/node/node.cpp b/app/node/node.cpp index 78613fa9b..840406ed8 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -537,6 +537,25 @@ bool Node::OutputsTo(const QString &id, bool recursively) const return false; } +bool Node::OutputsTo(NodeInput *input, bool recursively) const +{ + QList 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 inputs = GetInputsIncludingArrays(); diff --git a/app/node/node.h b/app/node/node.h index 91209581a..11a69704f 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -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 */ diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp index b9f90e5e4..e62c94a57 100644 --- a/app/render/backend/renderbackend.cpp +++ b/app/render/backend/renderbackend.cpp @@ -195,27 +195,37 @@ std::list 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 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;iparentNode()); + // If we're here, we must have this node. Determine if we're already copying a "parent" of this + for (int i=0; iparentNode()->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) diff --git a/app/render/backend/renderbackend.h b/app/render/backend/renderbackend.h index 8f9339691..59efca316 100644 --- a/app/render/backend/renderbackend.h +++ b/app/render/backend/renderbackend.h @@ -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); diff --git a/app/task/cache/cache.cpp b/app/task/cache/cache.cpp index 563dec1b3..411a6d96a 100644 --- a/app/task/cache/cache.cpp +++ b/app/task/cache/cache.cpp @@ -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 diff --git a/app/task/cache/cache.h b/app/task/cache/cache.h index bab3cffc8..c17344713 100644 --- a/app/task/cache/cache.h +++ b/app/task/cache/cache.h @@ -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_; diff --git a/app/task/render/render.cpp b/app/task/render/render.cpp index f0da1ff54..76b54ea98 100644 --- a/app/task/render/render.cpp +++ b/app/task/render/render.cpp @@ -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 audio_queue; std::list audio_lookup_table; @@ -93,11 +106,11 @@ void RenderTask::Render(const TimeRangeList& video_range, if (!video_range.isEmpty()) { QList 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 > hash_future = backend_.Hash(times); + QFuture > hash_future = backend_->Hash(times); hashes = hash_future.result(); for (int i=0;ivideo_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) diff --git a/app/task/render/render.h b/app/task/render/render.h index 9f9dd49e2..978a45982 100644 --- a/app/task/render/render.h +++ b/app/task/render/render.h @@ -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_;