diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index 737ca8770..af5bdfba5 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -1463,7 +1463,9 @@ FFmpegDecoderInstance::FFmpegDecoderInstance(const char *filename, int stream_in // Open codec error_code = avcodec_open2(codec_ctx_, codec, &opts_); if (error_code < 0) { - qDebug() << "Failed to open codec" << codec->id << error_code; + char buf[50]; + av_strerror(error_code, buf, 50); + qDebug() << "Failed to open codec" << codec->id << error_code << buf; ClearResources(); return; } diff --git a/app/common/memorypool.h b/app/common/memorypool.h index eae163381..b97f62fd7 100644 --- a/app/common/memorypool.h +++ b/app/common/memorypool.h @@ -57,6 +57,7 @@ public: */ MemoryPool(int element_count) { element_count_ = element_count; + ignore_arena_empty_signal_ = false; } /** @@ -65,6 +66,7 @@ public: * Deletes all arenas. */ virtual ~MemoryPool() { + ignore_arena_empty_signal_ = true; qDeleteAll(arenas_); } @@ -112,7 +114,7 @@ public: * Automatically releases this element's memory back to the arena it was retrieved from. */ ~Element() { - parent_->Release(this); + release(); } DISABLE_COPY_MOVE(Element) @@ -151,6 +153,13 @@ public: return accessed_; } + void release() { + if (data_) { + parent_->Release(this); + data_ = nullptr; + } + } + private: Arena* parent_; @@ -176,11 +185,13 @@ public: Arena(MemoryPool* parent) { parent_ = parent; data_ = nullptr; - use_count_ = 0; } ~Arena() { - // FIXME: Invalidate elements that have been lent out? + QLinkedList copy = lent_elements_; + foreach (Element* e, copy) { + e->release(); + } delete [] data_; } @@ -197,8 +208,11 @@ public: if (available_.at(i)) { // This buffer is available available_.replace(i, false); - use_count_++; - return std::make_shared(this, reinterpret_cast(data_ + i * element_sz_)); + + ElementPtr e = std::make_shared(this, + reinterpret_cast(data_ + i * element_sz_)); + lent_elements_.append(e.get()); + return e; } } @@ -215,17 +229,18 @@ public: int index = diff / element_sz_; available_.replace(index, true); - use_count_--; - if (!use_count_) { + lent_elements_.removeOne(e); + + if (lent_elements_.isEmpty()) { locker.unlock(); parent_->ArenaIsEmpty(this); } } - const int& GetUsageCount() { + int GetUsageCount() { QMutexLocker locker(&lock_); - return use_count_; + return lent_elements_.size(); } bool Allocate(size_t ele_sz, size_t nb_elements) { @@ -242,7 +257,6 @@ public: return true; } else { available_.clear(); - data_ = nullptr; return false; } @@ -267,7 +281,7 @@ public: size_t element_sz_; - int use_count_; + QLinkedList lent_elements_; }; @@ -317,6 +331,11 @@ public: } void ArenaIsEmpty(Arena* a) { + // FIXME: Does this need to be mutexed? + if (ignore_arena_empty_signal_) { + return; + } + QMutexLocker locker(&lock_); if (!a->GetUsageCount()) { @@ -343,6 +362,8 @@ private: QMutex lock_; + bool ignore_arena_empty_signal_; + }; OLIVE_NAMESPACE_EXIT diff --git a/app/common/qtutils.h b/app/common/qtutils.h index 10f30f21b..fb405b6b7 100644 --- a/app/common/qtutils.h +++ b/app/common/qtutils.h @@ -36,8 +36,9 @@ OLIVE_NAMESPACE_ENTER /** * @brief Retrieves the width of a string according to certain QFontMetrics * - * QFontMetrics::width() has been deprecatd in favor of QFontMetrics::horizontalAdvance(), but the latter was only - * introduced in 5.11+. This function wraps the latter for 5.11+ and the former for earlier. + * QFontMetrics::width() has been deprecatd in favor of QFontMetrics::horizontalAdvance(), but the + * latter was only introduced in 5.11+. This function wraps the latter for 5.11+ and the former for + * earlier. */ int QFontMetricsWidth(QFontMetrics fm, const QString& s); diff --git a/app/node/output/track/tracklist.cpp b/app/node/output/track/tracklist.cpp index 6165c11fb..0c7feb627 100644 --- a/app/node/output/track/tracklist.cpp +++ b/app/node/output/track/tracklist.cpp @@ -32,8 +32,8 @@ TrackList::TrackList(ViewerOutput *parent, const Timeline::TrackType &type, Node track_input_(track_input), type_(type) { - connect(track_input, &NodeInputArray::SubParamEdgeAdded, this, &TrackList::TrackConnected); - connect(track_input, &NodeInputArray::SubParamEdgeRemoved, this, &TrackList::TrackDisconnected); + connect(track_input_, &NodeInputArray::SubParamEdgeAdded, this, &TrackList::TrackConnected); + connect(track_input_, &NodeInputArray::SubParamEdgeRemoved, this, &TrackList::TrackDisconnected); } const Timeline::TrackType &TrackList::type() const @@ -192,6 +192,7 @@ void TrackList::TrackConnected(NodeEdgePtr edge) connect(connected_track, &TrackOutput::TrackLengthChanged, this, &TrackList::UpdateTotalLength); connect(connected_track, &TrackOutput::TrackHeightChanged, this, &TrackList::TrackHeightChangedSlot); + qDebug() << "Setting track type" << connected_track << type_; connected_track->set_track_type(type_); emit TrackListChanged(); diff --git a/app/render/backend/CMakeLists.txt b/app/render/backend/CMakeLists.txt index bff0940f1..25541f319 100644 --- a/app/render/backend/CMakeLists.txt +++ b/app/render/backend/CMakeLists.txt @@ -30,7 +30,6 @@ set(OLIVE_SOURCES render/backend/renderworker.h render/backend/renderworker.cpp - render/backend/rendercache.h render/backend/colorprocessorcache.h render/backend/decodercache.h diff --git a/app/render/backend/colorprocessorcache.h b/app/render/backend/colorprocessorcache.h index d247e03d5..065fc4c06 100644 --- a/app/render/backend/colorprocessorcache.h +++ b/app/render/backend/colorprocessorcache.h @@ -23,11 +23,10 @@ #include "project/item/footage/stream.h" #include "render/colorprocessor.h" -#include "rendercache.h" OLIVE_NAMESPACE_ENTER -using ColorProcessorCache = RenderCache; +using ColorProcessorCache = QHash; OLIVE_NAMESPACE_EXIT diff --git a/app/render/backend/decodercache.h b/app/render/backend/decodercache.h index 9f3939baf..6e0890f43 100644 --- a/app/render/backend/decodercache.h +++ b/app/render/backend/decodercache.h @@ -23,21 +23,10 @@ #include "codec/decoder.h" #include "project/item/footage/stream.h" -#include "rendercache.h" OLIVE_NAMESPACE_ENTER -class DecoderCache : public RenderCache -{ -public: - DecoderCache() = default; - - QMutex* lock() {return &lock_;} - -private: - QMutex lock_; - -}; +using DecoderCache = QHash; OLIVE_NAMESPACE_EXIT diff --git a/app/render/backend/opengl/openglbackend.cpp b/app/render/backend/opengl/openglbackend.cpp index cc67d33da..bd9095589 100644 --- a/app/render/backend/opengl/openglbackend.cpp +++ b/app/render/backend/opengl/openglbackend.cpp @@ -53,7 +53,7 @@ RenderWorker *OpenGLBackend::CreateNewWorker() } } - return new OpenGLWorker(proxy_); + return new OpenGLWorker(this, proxy_); } void OpenGLBackend::ClearProxy() diff --git a/app/render/backend/opengl/openglcolorprocessor.h b/app/render/backend/opengl/openglcolorprocessor.h index 0de0589ea..bd8e6dc9c 100644 --- a/app/render/backend/opengl/openglcolorprocessor.h +++ b/app/render/backend/opengl/openglcolorprocessor.h @@ -22,7 +22,6 @@ #define OPENGLCOLORPROCESSOR_H #include "openglshader.h" -#include "render/backend/rendercache.h" #include "render/colorprocessor.h" OLIVE_NAMESPACE_ENTER @@ -63,7 +62,7 @@ private slots: }; -using OpenGLColorProcessorCache = RenderCache; +using OpenGLColorProcessorCache = QHash; OLIVE_NAMESPACE_EXIT diff --git a/app/render/backend/opengl/openglproxy.cpp b/app/render/backend/opengl/openglproxy.cpp index 3e125d0a1..cd961c353 100644 --- a/app/render/backend/opengl/openglproxy.cpp +++ b/app/render/backend/opengl/openglproxy.cpp @@ -74,13 +74,13 @@ NodeValue OpenGLProxy::FrameToValue(FramePtr frame, StreamPtr stream, const Vide // Set up OCIO context QString colorspace_match = video_stream->get_colorspace_match_string(); - OpenGLColorProcessorPtr color_processor = std::static_pointer_cast(color_cache_.Get(colorspace_match)); + OpenGLColorProcessorPtr color_processor = std::static_pointer_cast(color_cache_.value(colorspace_match)); if (!color_processor) { color_processor = OpenGLColorProcessor::Create(video_stream->footage()->project()->color_manager(), video_stream->colorspace(), video_stream->footage()->project()->color_manager()->GetReferenceColorSpace()); - color_cache_.Add(colorspace_match, color_processor); + color_cache_.insert(colorspace_match, color_processor); } ColorManager::OCIOMethod ocio_method = ColorManager::GetOCIOMethodForMode(params.mode()); @@ -172,7 +172,7 @@ NodeValue OpenGLProxy::FrameToValue(FramePtr frame, StreamPtr stream, const Vide void OpenGLProxy::Close() { - shader_cache_.Clear(); + shader_cache_.clear(); buffer_.Destroy(); copy_pipeline_ = nullptr; functions_ = nullptr; @@ -186,7 +186,7 @@ void OpenGLProxy::RunNodeAccelerated(const Node *node, NodeValueTable &output_params, const VideoRenderingParams& params) { - OpenGLShaderPtr shader = shader_cache_.Get(node->ShaderID(input_params)); + OpenGLShaderPtr shader = shader_cache_.value(node->ShaderID(input_params)); if (!shader) { // Since we have shader code, compile it now @@ -208,7 +208,7 @@ void OpenGLProxy::RunNodeAccelerated(const Node *node, shader->addShaderFromSourceCode(QOpenGLShader::Vertex, vert_code); shader->link(); - shader_cache_.Add(node->id(), shader); + shader_cache_.insert(node->id(), shader); } // Create the output textures diff --git a/app/render/backend/opengl/openglshadercache.h b/app/render/backend/opengl/openglshadercache.h index 7846aaf1b..27e4586e0 100644 --- a/app/render/backend/opengl/openglshadercache.h +++ b/app/render/backend/opengl/openglshadercache.h @@ -24,11 +24,10 @@ #include #include "openglshader.h" -#include "render/backend/rendercache.h" OLIVE_NAMESPACE_ENTER -using OpenGLShaderCache = RenderCache; +using OpenGLShaderCache = QHash; OLIVE_NAMESPACE_EXIT diff --git a/app/render/backend/opengl/openglworker.cpp b/app/render/backend/opengl/openglworker.cpp index 84e1eaac3..9d84840cc 100644 --- a/app/render/backend/opengl/openglworker.cpp +++ b/app/render/backend/opengl/openglworker.cpp @@ -22,7 +22,8 @@ OLIVE_NAMESPACE_ENTER -OpenGLWorker::OpenGLWorker(OpenGLProxy* proxy) : +OpenGLWorker::OpenGLWorker(RenderBackend *parent, OpenGLProxy* proxy) : + RenderWorker(parent), proxy_(proxy) { } diff --git a/app/render/backend/opengl/openglworker.h b/app/render/backend/opengl/openglworker.h index dcf08fae7..82057a034 100644 --- a/app/render/backend/opengl/openglworker.h +++ b/app/render/backend/opengl/openglworker.h @@ -29,7 +29,7 @@ OLIVE_NAMESPACE_ENTER class OpenGLWorker : public RenderWorker { public: - OpenGLWorker(OpenGLProxy* proxy); + OpenGLWorker(RenderBackend* parent, OpenGLProxy* proxy); protected: virtual void TextureToFrame(const QVariant& texture, FramePtr frame, const QMatrix4x4 &mat) const override; diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp index 8da5859d5..21d44b95f 100644 --- a/app/render/backend/renderbackend.cpp +++ b/app/render/backend/renderbackend.cpp @@ -32,6 +32,8 @@ OLIVE_NAMESPACE_ENTER RenderBackend::RenderBackend(QObject *parent) : QObject(parent), viewer_node_(nullptr), + video_instance_queuer_(0), + audio_instance_queuer_(0), divider_(1), render_mode_(RenderMode::kOnline), pix_fmt_(PixelFormat::PIX_FMT_RGBA32F), @@ -57,7 +59,7 @@ void RenderBackend::SetViewerNode(ViewerOutput *viewer_node) CancelQueue(); // Delete all of our copied nodes - foreach (RenderWorker* instance, instance_pool_) { + foreach (RenderWorker* instance, video_instance_pool_) { instance->Close(); } @@ -69,7 +71,7 @@ void RenderBackend::SetViewerNode(ViewerOutput *viewer_node) disconnect(viewer_node_->audio_playback_cache(), &AudioPlaybackCache::Invalidated, this, - &RenderBackend::AudioCallback); + &RenderBackend::AudioInvalidated); } // Set viewer node @@ -77,7 +79,7 @@ void RenderBackend::SetViewerNode(ViewerOutput *viewer_node) if (viewer_node_) { // Initiate instances with new node - foreach (RenderWorker* instance, instance_pool_) { + foreach (RenderWorker* instance, video_instance_pool_) { instance->Init(viewer_node_); } @@ -89,34 +91,36 @@ void RenderBackend::SetViewerNode(ViewerOutput *viewer_node) connect(viewer_node_->audio_playback_cache(), &AudioPlaybackCache::Invalidated, this, - &RenderBackend::AudioCallback); + &RenderBackend::AudioInvalidated); } } void RenderBackend::CancelQueue() { // FIXME: Implement something better than this... - thread_pool_.waitForDone(); + video_thread_pool_.waitForDone(); } -QFuture RenderBackend::Hash(const rational &time) +QFuture RenderBackend::Hash(const rational &time, bool block_for_update) { - return QtConcurrent::run(&thread_pool_, - GetInstanceFromPool(), + return QtConcurrent::run(&video_thread_pool_, + GetInstanceFromPool(video_instance_pool_, video_thread_pool_, video_instance_queuer_), &RenderWorker::Hash, - time); + time, + block_for_update); } -QFuture RenderBackend::RenderFrame(const rational &time, bool clear_queue) +QFuture RenderBackend::RenderFrame(const rational &time, bool clear_queue, bool block_for_update) { if (clear_queue) { - thread_pool_.clear(); + video_thread_pool_.clear(); } - return QtConcurrent::run(&thread_pool_, - GetInstanceFromPool(), + return QtConcurrent::run(&video_thread_pool_, + GetInstanceFromPool(video_instance_pool_, video_thread_pool_, video_instance_queuer_), &RenderWorker::RenderFrame, - time); + time, + block_for_update); } void RenderBackend::SetDivider(const int ÷r) @@ -146,19 +150,26 @@ void RenderBackend::SetVideoDownloadMatrix(const QMatrix4x4 &mat) void RenderBackend::NodeGraphChanged(NodeInput *source) { - QLinkedList::iterator i; - - for (i=instance_pool_.begin(); i!=instance_pool_.end(); i++) { - (*i)->Queue(source); + foreach (RenderWorker* worker, video_instance_pool_) { + worker->Queue(source); } } +void RenderBackend::UpdateInstance(RenderWorker *instance) +{ + instance->SetAvailable(false); + instance->ProcessQueue(); + instance->SetVideoParams(video_params()); + instance->SetAudioParams(audio_params()); + instance->SetVideoDownloadMatrix(video_download_matrix_); +} + void RenderBackend::Close() { CancelQueue(); - qDeleteAll(instance_pool_); - instance_pool_.clear(); + qDeleteAll(video_instance_pool_); + video_instance_pool_.clear(); } VideoRenderingParams RenderBackend::video_params() const @@ -171,60 +182,63 @@ AudioRenderingParams RenderBackend::audio_params() const return AudioRenderingParams(viewer_node_->audio_params(), sample_fmt_); } -RenderWorker *RenderBackend::GetInstanceFromPool() +RenderWorker *RenderBackend::GetInstanceFromPool(QVector &worker_pool, QThreadPool &thread_pool, int &instance_queuer) { RenderWorker* instance = nullptr; - QLinkedList::iterator i; - - for (i=instance_pool_.begin(); i!=instance_pool_.end(); i++) { - if ((*i)->IsAvailable()) { - instance = *i; + foreach (RenderWorker* worker, worker_pool) { + if (worker->IsAvailable()) { + instance = worker; break; } } if (!instance) { - instance = CreateNewWorker(); - instance_pool_.append(instance); + if (worker_pool.size() < thread_pool.maxThreadCount()) { + // Can create another instance + instance = CreateNewWorker(); + worker_pool.append(instance); - if (viewer_node_) { - instance->Init(viewer_node_); + if (viewer_node_) { + instance->Init(viewer_node_); + } + + connect(instance, + &RenderWorker::FinishedJob, + this, + &RenderBackend::WorkerFinished, + Qt::QueuedConnection); + } else { + instance = worker_pool.at(instance_queuer % worker_pool.size()); + instance_queuer++; } - - connect(instance, - &RenderWorker::FinishedJob, - this, - &RenderBackend::WorkerFinished, - Qt::QueuedConnection); } - instance->SetAvailable(false); - instance->ProcessQueue(); - instance->SetVideoParams(video_params()); - instance->SetAudioParams(audio_params()); - instance->SetVideoDownloadMatrix(video_download_matrix_); - return instance; } -void RenderBackend::AudioCallback() +void RenderBackend::AudioInvalidated(const TimeRange& r) { - qDebug() << "STUB"; - /* - AudioPlaybackCache* pb_cache = viewer_node_->audio_playback_cache(); - QThreadPool* thread_pool = audio_copy_map_.thread_pool(); + QFutureWatcher* watcher = new QFutureWatcher(); + connect(watcher, &QFutureWatcher::finished, this, &RenderBackend::AudioRendered); - while (!pb_cache->IsFullyValidated() - && thread_pool->activeThreadCount() < thread_pool->maxThreadCount()) { - // FIXME: Trigger a background audio thread - TimeRange r = viewer_node_->audio_playback_cache()->GetInvalidatedRanges().first(); + watcher->setFuture(QtConcurrent::run(&video_thread_pool_, + GetInstanceFromPool(audio_instance_pool_, audio_thread_pool_, audio_instance_queuer_), + &RenderWorker::RenderAudio, + r)); +} - qDebug() << "FIXME: Start rendering audio at:" << r; +void RenderBackend::AudioRendered() +{ + QFutureWatcher* watcher = static_cast*>(sender()); - break; + if (watcher->result()) { + qDebug() << "AUDIO: Received" << watcher->result()->sample_count_per_channel() << "samples!"; + } else { + qDebug() << "AUDIO: Received null"; } - */ + + watcher->deleteLater(); } void RenderBackend::WorkerFinished() diff --git a/app/render/backend/renderbackend.h b/app/render/backend/renderbackend.h index c5edc3e82..45b4c0bb5 100644 --- a/app/render/backend/renderbackend.h +++ b/app/render/backend/renderbackend.h @@ -47,12 +47,12 @@ public: /** * @brief Asynchronously generate a hash at a given time */ - QFuture Hash(const rational& time); + QFuture Hash(const rational& time, bool block_for_update); /** * @brief Asynchronously generate a frame at a given time */ - QFuture RenderFrame(const rational& time, bool clear_queue); + QFuture RenderFrame(const rational& time, bool clear_queue, bool block_for_update); void SetDivider(const int& divider); @@ -67,6 +67,8 @@ public: public slots: void NodeGraphChanged(NodeInput *source); + void UpdateInstance(OLIVE_NAMESPACE::RenderWorker* instance); + protected: virtual RenderWorker* CreateNewWorker() = 0; @@ -77,15 +79,22 @@ protected: AudioRenderingParams audio_params() const; private: - RenderWorker *GetInstanceFromPool(); + RenderWorker *GetInstanceFromPool(QVector& worker_pool, + QThreadPool& thread_pool, + int& instance_queuer); ViewerOutput* viewer_node_; RenderCancelDialog* cancel_dialog_; - QLinkedList instance_pool_; + QVector video_instance_pool_; + QVector audio_instance_pool_; - QThreadPool thread_pool_; + QThreadPool video_thread_pool_; + QThreadPool audio_thread_pool_; + + int video_instance_queuer_; + int audio_instance_queuer_; // VIDEO MEMBERS int divider_; @@ -107,7 +116,9 @@ private: QList footage_wait_info_; private slots: - void AudioCallback(); + void AudioInvalidated(const TimeRange &r); + + void AudioRendered(); void WorkerFinished(); diff --git a/app/render/backend/rendercache.h b/app/render/backend/rendercache.h deleted file mode 100644 index 8da4d6722..000000000 --- a/app/render/backend/rendercache.h +++ /dev/null @@ -1,52 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2019 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 . - -***/ - -#ifndef RENDERCACHE_H -#define RENDERCACHE_H - -#include -#include - -OLIVE_NAMESPACE_ENTER - -template -class RenderCache -{ -public: - RenderCache() = default; - - void Clear(){values_.clear();} - - void Add(K key, V val){values_.insert(key, val);} - - V Get(K key) const {return values_.value(key);} - - bool Has(K key) const {return values_.contains(key);} - - void Remove(K key) {values_.remove(key);} - -private: - QHash values_; - -}; - -OLIVE_NAMESPACE_EXIT - -#endif // RENDERCACHE_H diff --git a/app/render/backend/renderworker.cpp b/app/render/backend/renderworker.cpp index 685cc5364..f40636de5 100644 --- a/app/render/backend/renderworker.cpp +++ b/app/render/backend/renderworker.cpp @@ -25,10 +25,12 @@ #include "audio/sumsamples.h" #include "config/config.h" #include "node/block/clip/clip.h" +#include "renderbackend.h" OLIVE_NAMESPACE_ENTER -RenderWorker::RenderWorker() : +RenderWorker::RenderWorker(RenderBackend* parent) : + parent_(parent), viewer_(nullptr), available_(true) { @@ -39,12 +41,16 @@ RenderWorker::~RenderWorker() Close(); } -QByteArray RenderWorker::Hash(const rational &time) +QByteArray RenderWorker::Hash(const rational &time, bool block_for_update) { if (!viewer_) { return QByteArray(); } + QMutexLocker locker(&lock_); + + UpdateData(block_for_update); + QCryptographicHash hasher(QCryptographicHash::Sha1); // Embed video parameters into this hash @@ -60,14 +66,18 @@ QByteArray RenderWorker::Hash(const rational &time) return hasher.result(); } -FramePtr RenderWorker::RenderFrame(const rational &time) +FramePtr RenderWorker::RenderFrame(const rational &time, bool block_for_update) { if (!viewer_) { return nullptr; } - NodeValueTable table = GenerateTable(viewer_, - TimeRange(time, time + video_params_.time_base())); + QMutexLocker locker(&lock_); + + UpdateData(block_for_update); + + NodeValueTable table = ProcessInput(viewer_->texture_input(), + TimeRange(time, time + video_params_.time_base())); QVariant texture = table.Get(NodeParam::kTexture); @@ -89,10 +99,45 @@ FramePtr RenderWorker::RenderFrame(const rational &time) return frame; } +SampleBufferPtr RenderWorker::RenderAudio(const TimeRange &range) +{ + if (!viewer_) { + return nullptr; + } + + QMutexLocker locker(&lock_); + + UpdateData(true); + + NodeValueTable table = ProcessInput(viewer_->samples_input(), range); + + QVariant samples = table.Get(NodeParam::kSamples); + + return samples.value(); +} + +void RenderWorker::UpdateData(bool block_for_update) +{ + // FIXME: This is pretty trashy. It works, but it's not good. Should probably be changed at some + // point. + if (block_for_update) { + QMetaObject::invokeMethod(parent_, + "UpdateInstance", + Qt::BlockingQueuedConnection, + OLIVE_NS_ARG(RenderWorker*, this)); + } else { + parent_->UpdateInstance(this); + } +} + NodeValueTable RenderWorker::GenerateBlockTable(const TrackOutput *track, const TimeRange &range) { + qDebug() << "Hello from track" << track << "type" << track->track_type(); + if (track->track_type() == Timeline::kTrackTypeAudio) { + qDebug() << "Hello?"; + QList active_blocks = track->BlocksAtTimeRange(range); // All these blocks will need to output to a buffer so we create one here @@ -273,8 +318,8 @@ void RenderWorker::FootageProcessingEvent(StreamPtr stream, const TimeRange &inp NodeValue value; bool found_cache = false; - if (still_image_cache_.Has(stream.get())) { - CachedStill cs = still_image_cache_.Get(stream.get()); + if (still_image_cache_.contains(stream.get())) { + const CachedStill& cs = still_image_cache_[stream.get()]; if (cs.colorspace == colorspace_match && cs.alpha_is_associated == video_stream->premultiplied_alpha() @@ -283,7 +328,7 @@ void RenderWorker::FootageProcessingEvent(StreamPtr stream, const TimeRange &inp value = cs.texture; found_cache = true; } else { - still_image_cache_.Remove(stream.get()); + still_image_cache_.remove(stream.get()); } } @@ -291,11 +336,11 @@ void RenderWorker::FootageProcessingEvent(StreamPtr stream, const TimeRange &inp value = GetDataFromStream(stream, input_time); - still_image_cache_.Add(stream.get(), {value, - colorspace_match, - video_stream->premultiplied_alpha(), - video_params_.divider(), - time_match}); + still_image_cache_.insert(stream.get(), {value, + colorspace_match, + video_stream->premultiplied_alpha(), + video_params_.divider(), + time_match}); } @@ -323,7 +368,7 @@ DecoderPtr RenderWorker::ResolveDecoderFromInput(StreamPtr stream) { // Access a map of Node inputs and decoder instances and retrieve a frame! - DecoderPtr decoder = decoder_cache_.Get(stream.get()); + DecoderPtr decoder = decoder_cache_.value(stream.get()); if (!decoder && stream) { // Create a new Decoder here @@ -331,7 +376,7 @@ DecoderPtr RenderWorker::ResolveDecoderFromInput(StreamPtr stream) decoder->set_stream(stream); if (decoder->Open()) { - decoder_cache_.Add(stream.get(), decoder); + decoder_cache_.insert(stream.get(), decoder); } else { decoder = nullptr; qWarning() << "Failed to open decoder for" << stream->footage()->filename() diff --git a/app/render/backend/renderworker.h b/app/render/backend/renderworker.h index d51470a63..4461ca48f 100644 --- a/app/render/backend/renderworker.h +++ b/app/render/backend/renderworker.h @@ -26,15 +26,16 @@ #include "decodercache.h" #include "node/traverser.h" #include "node/output/viewer/viewer.h" -#include "render/backend/rendercache.h" OLIVE_NAMESPACE_ENTER +class RenderBackend; + class RenderWorker : public QObject, public NodeTraverser { Q_OBJECT public: - RenderWorker(); + RenderWorker(RenderBackend* parent); virtual ~RenderWorker() override; @@ -84,7 +85,7 @@ public: * * SHA-1 hash or empty QByteArray if no viewer node is set. */ - QByteArray Hash(const rational &time); + QByteArray Hash(const rational &time, bool block_for_update); /** * @brief Render the frame at this time @@ -97,11 +98,9 @@ public: * function will still return a blank frame with the same parameters. If no viewer node is set, * nullptr is returned. */ - FramePtr RenderFrame(const rational& time); + FramePtr RenderFrame(const rational& time, bool block_for_update); - SampleBufferPtr RenderAudio(ViewerOutput* viewer, - const TimeRange& range, - const SampleFormat::Format& sample_fmt); + SampleBufferPtr RenderAudio(const TimeRange& range); protected: virtual void TextureToFrame(const QVariant& texture, FramePtr frame, const QMatrix4x4 &mat) const = 0; @@ -130,6 +129,8 @@ signals: void FinishedJob(); private: + void UpdateData(bool block_for_update); + NodeValue GetDataFromStream(StreamPtr stream, const TimeRange& input_time); DecoderPtr ResolveDecoderFromInput(StreamPtr stream); @@ -138,6 +139,8 @@ private: Node *CopyNodeConnections(Node *src_node); void CopyNodeMakeConnection(NodeInput *src_input, NodeInput *dst_input); + RenderBackend* parent_; + VideoRenderingParams video_params_; AudioRenderingParams audio_params_; @@ -150,7 +153,7 @@ private: rational time; }; - RenderCache still_image_cache_; + QHash still_image_cache_; QMatrix4x4 video_download_matrix_; @@ -161,6 +164,8 @@ private: QHash copy_map_; bool available_; + QMutex lock_; + }; OLIVE_NAMESPACE_EXIT diff --git a/app/task/cache/cache.cpp b/app/task/cache/cache.cpp index 9f34ec875..60d8a551a 100644 --- a/app/task/cache/cache.cpp +++ b/app/task/cache/cache.cpp @@ -51,6 +51,11 @@ struct HashDownloadFuturePair { QFuture download_future; }; +struct HashTimePair { + rational time; + QByteArray hash; +}; + void CacheTask::Action() { OpenGLBackend backend; @@ -94,7 +99,7 @@ void CacheTask::Action() next = snapped + timebase; } - hash_list.append({snapped, backend.Hash(snapped)}); + hash_list.append({snapped, backend.Hash(snapped, false)}); range_to_cache.RemoveTimeRange(TimeRange(snapped, next)); } @@ -107,11 +112,32 @@ void CacheTask::Action() // Render all frames necessary QLinkedList render_lookup_table; { + QLinkedList sorted_times; + QLinkedList::iterator sorted_iterator; + + // Rendering is more efficient if we cache in order 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(); - render_lookup_table.append({hash, backend.RenderFrame(i.value().first(), false)}); + 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, false, false)}); } } @@ -124,6 +150,7 @@ void CacheTask::Action() // Start downloading frames that have finished { int counter = 0; + int nb_frames = render_lookup_table.size(); QLinkedList download_futures; @@ -160,7 +187,7 @@ void CacheTask::Action() // Signal process counter++; - emit ProgressChanged(qRound(100.0 * static_cast(counter) / static_cast(render_lookup_table.size()))); + emit ProgressChanged(qRound(100.0 * static_cast(counter) / static_cast(nb_frames))); j = download_futures.erase(j); } else { diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 1d3c393cb..e7edea9cc 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -418,7 +418,7 @@ void ViewerWidget::UpdateTextureFromNode(const rational& time) this, &ViewerWidget::RendererGeneratedFrame); - watcher->setFuture(GetFrame(time, true)); + watcher->setFuture(GetFrame(time, true, true)); } void ViewerWidget::PlayInternal(int speed, bool in_to_out_only) @@ -525,7 +525,7 @@ void ViewerWidget::FillPlaybackQueue() rational next_time = Timecode::timestamp_to_time(playback_queue_next_frame_, timebase()); playback_queue_next_frame_ += playback_speed_; - QFuture future = GetFrame(next_time, false); + QFuture future = GetFrame(next_time, false, false); future.waitForFinished(); playback_queue_.AppendTimewise({future.result()->timestamp(), future.result()}, playback_speed_); @@ -552,7 +552,7 @@ QString ViewerWidget::GetCachedFilenameFromTime(const rational &time) bool ViewerWidget::FrameExistsAtTime(const rational &time) { - return GetConnectedNode() && time < GetConnectedNode()->GetLength(); + return GetConnectedNode() && time >= 0 && time < GetConnectedNode()->GetLength(); } void ViewerWidget::SetDisplayImage(FramePtr frame, bool main_only) @@ -584,7 +584,7 @@ void ViewerWidget::RequestNextFrameForQueue() &QFutureWatcher::finished, this, &ViewerWidget::RendererGeneratedFrameForQueue); - watcher->setFuture(GetFrame(next_time, false)); + watcher->setFuture(GetFrame(next_time, false, true)); } PixelFormat::Format ViewerWidget::GetCurrentPixelFormat() const @@ -592,12 +592,12 @@ PixelFormat::Format ViewerWidget::GetCurrentPixelFormat() const return PixelFormat::instance()->GetConfiguredFormatForMode(RenderMode::kOffline); } -QFuture ViewerWidget::GetFrame(const rational &t, bool clear_render_queue) +QFuture ViewerWidget::GetFrame(const rational &t, bool clear_render_queue, bool block_update) { QByteArray cached_hash = GetConnectedNode()->video_frame_cache()->GetHash(t); if (cached_hash.isEmpty()) { // Frame hasn't been cached, start render job - return renderer_->RenderFrame(t, clear_render_queue); + return renderer_->RenderFrame(t, clear_render_queue, block_update); } else { // Frame has been cached, grab the frame QString cache_fn = GetConnectedNode()->video_frame_cache()->CachePathName(cached_hash, diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index 9958c1ef8..408d34ef9 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -188,7 +188,7 @@ private: PixelFormat::Format GetCurrentPixelFormat() const; - QFuture GetFrame(const rational& t, bool clear_render_queue); + QFuture GetFrame(const rational& t, bool clear_render_queue, bool block_update); QStackedWidget* stack_; diff --git a/app/widget/viewer/viewerqueue.h b/app/widget/viewer/viewerqueue.h index 6c3704867..7a449e2b7 100644 --- a/app/widget/viewer/viewerqueue.h +++ b/app/widget/viewer/viewerqueue.h @@ -38,17 +38,18 @@ public: void AppendTimewise(const ViewerPlaybackFrame& f, int playback_speed) { - if (this->isEmpty() || f.timestamp > this->last().timestamp == playback_speed > 0) { + reverse_iterator i = this->rbegin(); + + if (this->isEmpty() || (this->last().timestamp < f.timestamp) == (playback_speed > 0)) { this->append(f); } else { - reverse_iterator i = this->rbegin(); - while (i != this->rend()) { reverse_iterator next = i + 1; - if (next->timestamp < f.timestamp == playback_speed > 0) { + if ((next->timestamp < f.timestamp) == (playback_speed > 0)) { this->insert(i.base(), f); - break; + + return; } else { i = next; }