diff --git a/app/config/config.cpp b/app/config/config.cpp index cd0b7edc0..479f5b923 100644 --- a/app/config/config.cpp +++ b/app/config/config.cpp @@ -83,7 +83,7 @@ void Config::SetDefaults() config_map_["DropWithoutSequenceBehavior"] = TimelineWidget::kDWSAsk; config_map_["Loop"] = false; - config_map_["AutoCache"] = true;//false;//true; + config_map_["AutoCache"] = true; config_map_["AutoCacheInterval"] = 1000; config_map_["NodeCatColor0"] = QVariant::fromValue(Color(0.75f, 0.75f, 0.75f)); diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp index 26d575f25..f92385327 100644 --- a/app/render/backend/renderbackend.cpp +++ b/app/render/backend/renderbackend.cpp @@ -49,7 +49,10 @@ void RenderBackend::SetViewerNode(ViewerOutput *viewer_node) return; } - if (viewer_node_) { + ViewerOutput* old_viewer = viewer_node_; + viewer_node_ = viewer_node; + + if (old_viewer) { // Delete all of our copied nodes pool_.clear(); pool_.waitForDone(); @@ -61,19 +64,19 @@ void RenderBackend::SetViewerNode(ViewerOutput *viewer_node) render_queue_.clear(); // Delete all the nodes + foreach (Node* c, copy_map_) { + c->DisconnectAll(); + } qDeleteAll(copy_map_); copy_map_.clear(); copied_viewer_node_ = nullptr; - disconnect(viewer_node_, + disconnect(old_viewer, &ViewerOutput::GraphChangedFrom, this, &RenderBackend::NodeGraphChanged); } - // Set viewer node - viewer_node_ = viewer_node; - if (viewer_node_) { // Copy graph copied_viewer_node_ = static_cast(viewer_node_->copy()); @@ -107,7 +110,7 @@ void RenderBackend::ClearVideoQueue() QFuture > RenderBackend::Hash(const QList ×) { - return QtConcurrent::run([this](const QList ×){ + return QtConcurrent::run(&pool_, [this](const QList ×){ QList hashes; foreach (const rational& t, times) { @@ -155,6 +158,8 @@ RenderTicketPtr RenderBackend::RenderAudio(const TimeRange &r) render_queue_.append(ticket); + RunNextJob(); + return ticket; } @@ -294,14 +299,16 @@ void RenderBackend::RunNextJob() switch (ticket->GetType()) { case RenderTicket::kTypeVideo: - QtConcurrent::run(worker, + QtConcurrent::run(&pool_, + worker, &RenderWorker::RenderFrame, ticket, copied_viewer_node_, ticket->GetTime().in()); break; case RenderTicket::kTypeAudio: - QtConcurrent::run(worker, + QtConcurrent::run(&pool_, + worker, &RenderWorker::RenderAudio, ticket, copied_viewer_node_, @@ -319,9 +326,15 @@ 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()); } void RenderBackend::WorkerFinished() @@ -336,7 +349,11 @@ void RenderBackend::WorkerFinished() } } - RunNextJob(); + if (viewer_node_) { + RunNextJob(); + } else { + qDebug() << "Ignored job finish because no viewer"; + } } void RenderBackend::CopyNodeInputValue(NodeInput *input) diff --git a/app/render/backend/renderbackend.h b/app/render/backend/renderbackend.h index 0025662b2..f428f09fe 100644 --- a/app/render/backend/renderbackend.h +++ b/app/render/backend/renderbackend.h @@ -57,12 +57,10 @@ public: */ RenderTicketPtr RenderFrame(const rational& time); - QFuture RenderFrames(const QList& frames); - /** * @brief Asynchronously generate a chunk of audio */ - QFuture RenderAudio(const TimeRange& r); + RenderTicketPtr RenderAudio(const TimeRange& r); void SetVideoParams(const VideoRenderingParams& params); diff --git a/app/render/backend/renderworker.cpp b/app/render/backend/renderworker.cpp index f6864d266..a022a1f37 100644 --- a/app/render/backend/renderworker.cpp +++ b/app/render/backend/renderworker.cpp @@ -60,7 +60,7 @@ void RenderWorker::RenderFrame(RenderTicketPtr ticket, ViewerOutput* viewer, con ticket->Finish(QVariant::fromValue(frame)); - FinishedJob(); + emit FinishedJob(); } void RenderWorker::RenderAudio(RenderTicketPtr ticket, ViewerOutput* viewer, const TimeRange &range) @@ -71,7 +71,7 @@ void RenderWorker::RenderAudio(RenderTicketPtr ticket, ViewerOutput* viewer, con ticket->Finish(samples); - FinishedJob(); + emit FinishedJob(); } NodeValueTable RenderWorker::GenerateBlockTable(const TrackOutput *track, const TimeRange &range) diff --git a/app/task/cache/cache.cpp b/app/task/cache/cache.cpp index 348b21046..fd5b1985e 100644 --- a/app/task/cache/cache.cpp +++ b/app/task/cache/cache.cpp @@ -37,18 +37,20 @@ CacheTask::CacheTask(ViewerOutput* viewer, const VideoRenderingParams& vparams, bool CacheTask::Run() { // Get list of invalidated ranges - TimeRangeList range_to_cache = viewer()->video_frame_cache()->GetInvalidatedRanges(); + TimeRangeList video_range = viewer()->video_frame_cache()->GetInvalidatedRanges(); + TimeRangeList audio_range = viewer()->audio_playback_cache()->GetInvalidatedRanges(); // If we're caching only in-out, limit the range to that if (in_out_only_) { Sequence* s = static_cast(viewer()->parent()); if (s->workarea()->enabled()) { - range_to_cache = range_to_cache.Intersects(s->workarea()->range()); + video_range = video_range.Intersects(s->workarea()->range()); + audio_range = audio_range.Intersects(s->workarea()->range()); } } - Render(range_to_cache, QMatrix4x4(), false, true); + Render(video_range, audio_range, QMatrix4x4(), true); download_threads_.waitForDone(); @@ -67,4 +69,9 @@ void CacheTask::FrameDownloaded(const QByteArray &hash, const QLinkedListaudio_playback_cache()->WritePCM(range, samples); +} + OLIVE_NAMESPACE_EXIT diff --git a/app/task/cache/cache.h b/app/task/cache/cache.h index d49afa53e..66d4a4661 100644 --- a/app/task/cache/cache.h +++ b/app/task/cache/cache.h @@ -44,6 +44,8 @@ protected: virtual void FrameDownloaded(const QByteArray& hash, const QLinkedList& times) override; + virtual void AudioDownloaded(const TimeRange& range, SampleBufferPtr samples) override; + private: bool in_out_only_; diff --git a/app/task/export/export.cpp b/app/task/export/export.cpp index 587af6ab1..3371b2c49 100644 --- a/app/task/export/export.cpp +++ b/app/task/export/export.cpp @@ -85,7 +85,17 @@ bool ExportTask::Run() } // Start render process - Render({range}, mat, params_.audio_enabled(), false); + TimeRangeList video_range, audio_range; + + if (params_.video_enabled()) { + video_range.append(range); + } + + if (params_.audio_enabled()) { + audio_range.append(range); + } + + Render(video_range, audio_range, mat, false); bool success = true; @@ -167,8 +177,6 @@ void ExportTask::AudioDownloaded(const TimeRange &range, SampleBufferPtr samples adjusted_range -= params_.custom_range().in(); } - qDebug() << "Downloaded audio" << adjusted_range; - audio_data_.WritePCM(adjusted_range, samples); } diff --git a/app/task/render/render.cpp b/app/task/render/render.cpp index ff799f104..a83554871 100644 --- a/app/task/render/render.cpp +++ b/app/task/render/render.cpp @@ -57,81 +57,25 @@ struct HashDownloadFuturePair { QFuture download_future; }; -void RenderTask::Render(const TimeRangeList& range_to_cache, +void RenderTask::Render(const TimeRangeList& video_range, + const TimeRangeList &audio_range, const QMatrix4x4& mat, - bool audio_enabled, bool use_disk_cache) { OpenGLBackend backend; backend.moveToThread(qApp->thread()); + // 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.SetVideoDownloadMatrix(mat); - // Get hashes for each frame and group likes together - QMap< QByteArray, QLinkedList > times_to_render; - - { - QList times = viewer_->video_frame_cache()->GetFrameListFromTimeRange(range_to_cache); - - QFuture > hash_future = backend.Hash(times); - QList hashes = hash_future.result(); - - // Determine any duplicates - int index = 0; - foreach (const QByteArray& hash, hashes) { - const rational& time = times.at(index); - - if (use_disk_cache - && QFileInfo::exists(viewer_->video_frame_cache()->CachePathName(hash, video_params_.format()))) { - // Already exists, no need to render it again - FrameDownloaded(hash, {time}); - } else { - times_to_render[hash].append(time); - } - - index++; - } - } - - // Render all frames necessary - QLinkedList render_lookup_table; - { - QLinkedList sorted_times; - QLinkedList::iterator sorted_iterator; - - // Rendering is more efficient if we cache in order, so here we sort - QMap< QByteArray, QLinkedList >::const_iterator i; - for (i=times_to_render.constBegin(); i!=times_to_render.constEnd(); i++) { - const QByteArray& hash = i.key(); - const rational& time = i.value().first(); - - bool inserted = false; - - for (sorted_iterator=sorted_times.begin(); sorted_iterator!=sorted_times.end(); sorted_iterator++) { - if (sorted_iterator->time > time) { - sorted_times.insert(sorted_iterator, {time, hash}); - inserted = true; - break; - } - } - - if (!inserted) { - sorted_times.append({time, hash}); - } - } - - foreach (const HashTimePair& p, sorted_times) { - render_lookup_table.append({p.hash, backend.RenderFrame(p.time)}); - } - } - QLinkedList audio_lookup_table; - if (audio_enabled) { - foreach (const TimeRange& r, range_to_cache) { + if (!audio_range.isEmpty()) { + foreach (const TimeRange& r, audio_range) { QList ranges = RenderBackend::SplitRangeIntoChunks(r); foreach (const TimeRange& split, ranges) { @@ -140,6 +84,66 @@ void RenderTask::Render(const TimeRangeList& range_to_cache, } } + // Get hashes for each frame and group likes together + QMap< QByteArray, QLinkedList > times_to_render; + QLinkedList render_lookup_table; + if (!video_range.isEmpty()) { + + { + QList times = viewer_->video_frame_cache()->GetFrameListFromTimeRange(video_range); + + QFuture > hash_future = backend.Hash(times); + QList hashes = hash_future.result(); + + // Determine any duplicates + int index = 0; + foreach (const QByteArray& hash, hashes) { + const rational& time = times.at(index); + + if (use_disk_cache + && QFileInfo::exists(viewer_->video_frame_cache()->CachePathName(hash, video_params_.format()))) { + // Already exists, no need to render it again + FrameDownloaded(hash, {time}); + } else { + times_to_render[hash].append(time); + } + + index++; + } + } + + // Render all frames necessary + { + QLinkedList sorted_times; + QLinkedList::iterator sorted_iterator; + + // Rendering is more efficient if we cache in order, so here we sort + QMap< QByteArray, QLinkedList >::const_iterator i; + for (i=times_to_render.constBegin(); i!=times_to_render.constEnd(); i++) { + const QByteArray& hash = i.key(); + const rational& time = i.value().first(); + + bool inserted = false; + + for (sorted_iterator=sorted_times.begin(); sorted_iterator!=sorted_times.end(); sorted_iterator++) { + if (sorted_iterator->time > time) { + sorted_times.insert(sorted_iterator, {time, hash}); + inserted = true; + break; + } + } + + if (!inserted) { + sorted_times.append({time, hash}); + } + } + + foreach (const HashTimePair& p, sorted_times) { + render_lookup_table.append({p.hash, backend.RenderFrame(p.time)}); + } + } + } + // Start downloading frames that have finished int counter = 0; @@ -159,7 +163,7 @@ void RenderTask::Render(const TimeRangeList& range_to_cache, i = render_lookup_table.begin(); - while (i != render_lookup_table.end()) { + while (!IsCancelled() && i != render_lookup_table.end()) { if (i->frame_future->IsFinished()) { FramePtr f = i->frame_future->Get().value(); @@ -174,7 +178,7 @@ void RenderTask::Render(const TimeRangeList& range_to_cache, j = download_futures.begin(); - while (j != download_futures.end()) { + while (!IsCancelled() && j != download_futures.end()) { if (j->download_future.isFinished()) { // Place it in the cache FrameDownloaded(j->hash, times_to_render.value(j->hash)); @@ -189,26 +193,20 @@ void RenderTask::Render(const TimeRangeList& range_to_cache, } } - if (audio_enabled) { - k = audio_lookup_table.begin(); + k = audio_lookup_table.begin(); - while (k != audio_lookup_table.end()) { - if (k->sample_future->IsFinished()) { - AudioDownloaded(k->range, k->sample_future->Get().value()); + while (!IsCancelled() && k != audio_lookup_table.end()) { + if (k->sample_future->IsFinished()) { + AudioDownloaded(k->range, k->sample_future->Get().value()); - k = audio_lookup_table.erase(k); - } else { - k++; - } + k = audio_lookup_table.erase(k); + } else { + k++; } } } } -void RenderTask::AudioDownloaded(const TimeRange &, SampleBufferPtr) -{ -} - void RenderTask::SetAnchorPoint(const rational &r) { anchor_point_ = r; diff --git a/app/task/render/render.h b/app/task/render/render.h index d7407c97e..8f525f14b 100644 --- a/app/task/render/render.h +++ b/app/task/render/render.h @@ -34,13 +34,16 @@ public: RenderTask(ViewerOutput* viewer, const VideoRenderingParams &vparams, const AudioRenderingParams &aparams); protected: - void Render(const TimeRangeList &range_to_cache, const QMatrix4x4 &mat, bool audio_enabled, bool use_disk_cache); + void Render(const TimeRangeList &video_range, + const TimeRangeList &audio_range, + const QMatrix4x4 &mat, + bool use_disk_cache); virtual QFuture DownloadFrame(FramePtr frame, const QByteArray &hash) = 0; virtual void FrameDownloaded(const QByteArray& hash, const QLinkedList& times) = 0; - virtual void AudioDownloaded(const TimeRange& range, SampleBufferPtr samples); + virtual void AudioDownloaded(const TimeRange& range, SampleBufferPtr samples) = 0; ViewerOutput* viewer() const {