From 0538c01f45c57862eee07c32bea1d40c1bd12995 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 9 May 2022 18:22:32 -0700 Subject: [PATCH] remove hashtraverser --- app/node/CMakeLists.txt | 2 - app/node/hashtraverser.cpp | 158 ----------------------------- app/node/hashtraverser.h | 70 ------------- app/node/value.cpp | 48 --------- app/node/value.h | 9 -- app/render/previewautocacher.cpp | 142 +++----------------------- app/render/previewautocacher.h | 24 ----- app/render/rendermanager.cpp | 17 +--- app/render/rendermanager.h | 7 +- app/task/export/export.cpp | 16 ++- app/task/export/export.h | 2 +- app/task/precache/precachetask.cpp | 5 +- app/task/precache/precachetask.h | 2 +- app/task/render/render.cpp | 76 +++----------- app/task/render/render.h | 6 +- 15 files changed, 42 insertions(+), 542 deletions(-) delete mode 100644 app/node/hashtraverser.cpp delete mode 100644 app/node/hashtraverser.h diff --git a/app/node/CMakeLists.txt b/app/node/CMakeLists.txt index 3b66b8129..ed354586d 100644 --- a/app/node/CMakeLists.txt +++ b/app/node/CMakeLists.txt @@ -38,8 +38,6 @@ set(OLIVE_SOURCES node/globals.h node/graph.cpp node/graph.h - node/hashtraverser.cpp - node/hashtraverser.h node/inputdragger.cpp node/inputdragger.h node/inputimmediate.cpp diff --git a/app/node/hashtraverser.cpp b/app/node/hashtraverser.cpp deleted file mode 100644 index 28948cef8..000000000 --- a/app/node/hashtraverser.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2021 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 . - -***/ - -#include "hashtraverser.h" - -#include - -#include "common/filefunctions.h" - -namespace olive { - -#define super NodeTraverser - -HashTraverser::HashTraverser() : - hash_(QCryptographicHash::Sha1) // Appears to be the fastest hashing algorithm -{ -} - -QByteArray HashTraverser::GetHash(const Node *node, const Node::ValueHint &hint, const VideoParams ¶ms, const TimeRange &range) -{ - // Reset hash - hash_.reset(); - texture_ids_.clear(); - - // Set params throughout traverser - SetCacheVideoParams(params); - - // Embed video parameters into this hash - Hash(params.effective_width()); - Hash(params.effective_height()); - Hash(params.format()); - Hash(params.interlacing()); - //Hash(reference); - - // Our overrides will generate a hash from this - NodeValueTable table = GenerateTable(node, hint, range); - NodeValue final_value = GenerateRowValueElement(hint, NodeValue::kTexture, &table); - HashNodeValue(final_value); - - // Return the hash - return hash_.result(); -} - -void HashTraverser::ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time) -{ - Hash(FileFunctions::GetUniqueFileIdentifier(stream.filename())); - Hash(stream.loop_mode()); - Hash(stream.video_params().stream_index()); - Hash(stream.video_params().colorspace()); - Hash(stream.video_params().premultiplied_alpha()); - Hash(GetCacheVideoParams().divider()); - Hash(stream.video_params().video_type() == VideoParams::kVideoTypeStill ? 0 : input_time); - Hash(stream.video_params().video_type()); - - texture_ids_.insert(destination.get(), hash_.result()); -} - -void HashTraverser::ProcessAudioFootage(SampleBuffer &destination, const FootageJob &stream, const TimeRange &input_time) -{ -} - -void HashTraverser::ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob &job) -{ - HashGenerateJob(node, &job); - - Hash(job.GetShaderID()); - Hash(job.GetIterativeInput()); - Hash(job.GetIterationCount()); - - for (auto it=job.GetInterpolationMap().cbegin(); it!=job.GetInterpolationMap().cend(); it++) { - Hash(it.key()); - Hash(it.value()); - } - - texture_ids_.insert(destination.get(), hash_.result()); -} - -void HashTraverser::ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob &job) -{ - Hash(job.GetColorProcessor()->id()); - texture_ids_.insert(destination.get(), hash_.result()); -} - -void HashTraverser::ProcessSamples(SampleBuffer &destination, const Node *node, const TimeRange &range, const SampleJob &job) -{ -} - -void HashTraverser::ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob &job) -{ - HashGenerateJob(node, &job); - - texture_ids_.insert(destination.get(), hash_.result()); -} - -void HashTraverser::HashGenerateJob(const Node *node, const GenerateJob *job) -{ - Hash(node->id()); - Hash(job->GetAlphaChannelRequired()); - - for (auto it=job->GetValues().cbegin(); it!=job->GetValues().cend(); it++) { - Hash(it.key()); - HashNodeValue(it.value()); - } -} - -void HashTraverser::Hash(const QByteArray &array) -{ - hash_.addData(array); -} - -void HashTraverser::Hash(const QString &string) -{ - hash_.addData(string.toUtf8()); -} - -void HashTraverser::HashNodeValue(const NodeValue &value) -{ - NodeValue::Type value_type = value.type(); - - if (value_type == NodeValue::kSamples || value_type == NodeValue::kTexture) { - QByteArray id_for_buffer; - if (value_type == NodeValue::kTexture) { - TexturePtr texture = value.toTexture(); - id_for_buffer = texture_ids_.value(texture.get()); - } - - if (!id_for_buffer.isEmpty()) { - Hash(id_for_buffer); - } - } else { - Hash(NodeValue::ValueToBytes(value)); - } -} - -template -void HashTraverser::Hash(T value) -{ - hash_.addData(reinterpret_cast(&value), sizeof(value)); -} - -} diff --git a/app/node/hashtraverser.h b/app/node/hashtraverser.h deleted file mode 100644 index bdf0a70f4..000000000 --- a/app/node/hashtraverser.h +++ /dev/null @@ -1,70 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2021 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 HASHTRAVERSER_H -#define HASHTRAVERSER_H - -#include "traverser.h" - -namespace olive { - -class HashTraverser : public NodeTraverser -{ -public: - HashTraverser(); - - QByteArray GetHash(const Node *node, const Node::ValueHint &hint, const VideoParams ¶ms, const TimeRange &range); - -protected: - virtual void ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time) override; - - virtual void ProcessAudioFootage(SampleBuffer &destination, const FootageJob &stream, const TimeRange &input_time) override; - - virtual void ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob& job) override; - - virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob& job) override; - - virtual void ProcessSamples(SampleBuffer &destination, const Node *node, const TimeRange &range, const SampleJob &job) override; - - virtual void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob& job) override; - -private: - void HashGenerateJob(const Node *node, const GenerateJob *job); - - void HashFootageJob(); - - template - void Hash(T value); - - void Hash(const QByteArray &array); - - void Hash(const QString &string); - - void HashNodeValue(const NodeValue &value); - - QCryptographicHash hash_; - - QHash texture_ids_; - -}; - -} - -#endif // HASHTRAVERSER_H diff --git a/app/node/value.cpp b/app/node/value.cpp index 5b1355451..5cc7e9bc5 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -92,54 +92,6 @@ QString NodeValue::ValueToString(Type data_type, const QVariant &value, bool val } } -template -QByteArray ValueToBytesInternal(const QVariant &v) -{ - QByteArray bytes; - - int size_of_type = sizeof(T); - - bytes.resize(size_of_type); - T raw_val = v.value(); - memcpy(bytes.data(), &raw_val, static_cast(size_of_type)); - - return bytes; -} - -QByteArray NodeValue::ValueToBytes(NodeValue::Type type, const QVariant &value) -{ - switch (type) { - case kInt: return ValueToBytesInternal(value); - case kFloat: return ValueToBytesInternal(value); - case kColor: return ValueToBytesInternal(value); - case kText: return value.toString().toUtf8(); - case kBoolean: return ValueToBytesInternal(value); - case kFont: return value.toString().toUtf8(); - case kFile: return value.toString().toUtf8(); - case kMatrix: return ValueToBytesInternal(value); - case kRational: return ValueToBytesInternal(value); - case kVec2: return ValueToBytesInternal(value); - case kVec3: return ValueToBytesInternal(value); - case kVec4: return ValueToBytesInternal(value); - case kCombo: return ValueToBytesInternal(value); - case kBezier: return ValueToBytesInternal(value); - - case kVideoParams: - return value.value().toBytes(); - case kAudioParams: - return value.value().toBytes(); - - // These types have no persistent input - case kNone: - case kTexture: - case kSamples: - case kDataTypeCount: - break; - } - - return QByteArray(); -} - QVector NodeValue::split_normal_value_into_track_values(Type type, const QVariant &value) { QVector vals(get_number_of_keyframe_tracks(type)); diff --git a/app/node/value.h b/app/node/value.h index 49f977ad0..c22615112 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -257,15 +257,6 @@ public: static QVariant StringToValue(Type data_type, const QString &string, bool value_is_a_key_track); - /** - * @brief Convert a value from a NodeParam into bytes - */ - static QByteArray ValueToBytes(Type type, const QVariant& value); - static QByteArray ValueToBytes(const NodeValue &value) - { - return ValueToBytes(value.type(), value.data_); - } - static QVector split_normal_value_into_track_values(Type type, const QVariant &value); static QVariant combine_track_values_into_normal_value(Type type, const QVector& split); diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index 98e1e73d1..386a8c98c 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -93,40 +93,6 @@ RenderTicketPtr PreviewAutoCacher::GetRangeOfAudio(TimeRange range, bool priorit return RenderAudio(range, false, prioritize); } -QVector PreviewAutoCacher::GenerateHashes(ViewerOutput *viewer, FrameHashCache* cache, const QVector ×) -{ - QVector hash_data(times.size()); - - QVector existing_hashes; - - for (int i=0; iGetConnectedTextureOutput(), - viewer->GetConnectedTextureValueHint(), - viewer->GetVideoParams(), - time); - - // Check memory list since disk checking is slow - bool hash_exists = existing_hashes.contains(hash); - - if (!hash_exists) { - // FIXME: Using CachePathName here is NOT thread safe and should be replaced - hash_exists = QFileInfo::exists(cache->CachePathName(hash)); - - if (hash_exists) { - existing_hashes.push_back(hash); - } - } - - // Set hash in FrameHashCache's thread rather than in ours to prevent race conditions - hash_data[i] = {time, hash, hash_exists}; - } - - return hash_data; -} - void PreviewAutoCacher::VideoInvalidated(const TimeRange &range) { // Stop any current render tasks because a) they might be out of date now anyway, and b) we @@ -151,39 +117,6 @@ void PreviewAutoCacher::AudioInvalidated(const TimeRange &range) } } -void PreviewAutoCacher::HashesProcessed() -{ - // Receive watcher - QFutureWatcher< QVector >* watcher = static_cast >*>(sender()); - - // If the task list doesn't contain this watcher, presumably it was cleared as a result of a - // viewer switch, so we'll completely ignore this watcher - if (hash_tasks_.contains(watcher)) { - // Remove task from hash task list - hash_tasks_.removeOne(watcher); - - // Set all hashes we received that are still current - JobTime job_time = watcher->property("job").value(); - auto hashes = watcher->result(); - foreach (auto hash, hashes) { - if (video_job_tracker_.isCurrent(hash.time, job_time)) { - viewer_node_->video_frame_cache()->SetHash(hash.time, hash.hash, hash.exists); - } - } - - // RequeueFrames won't run if hash tasks isn't empty, so if it is, trigger it now - if (hash_tasks_.isEmpty()) { - delayed_requeue_timer_.stop(); - delayed_requeue_timer_.start(); - } - - // Continue rendering - TryRender(); - } - - delete watcher; -} - void PreviewAutoCacher::AudioRendered() { // Receive watcher @@ -271,24 +204,23 @@ void PreviewAutoCacher::VideoRendered() // If the task list doesn't contain this watcher, presumably it was cleared as a result of a // viewer switch, so we'll completely ignore this watcher - if (video_tasks_.contains(watcher)) { + if (video_tasks_.remove(watcher)) { // Assume that a "result" is a fully completed image and a non-result is a cancelled ticket - QByteArray hash = video_tasks_.take(watcher); - if (watcher->HasResult()) { + qDebug() << "FIXME: oops no frame downloading"; + + /* // Download frame in another thread - if (!hash.isEmpty()) { - FramePtr frame = watcher->Get().value(); - RenderTicketWatcher* w = new RenderTicketWatcher(); - w->setProperty("job", QVariant::fromValue(last_update_time_)); - w->setProperty("frame", QVariant::fromValue(frame)); - video_download_tasks_.insert(w, hash); - connect(w, &RenderTicketWatcher::Finished, this, &PreviewAutoCacher::VideoDownloaded); - w->SetTicket(RenderManager::instance()->SaveFrameToCache(viewer_node_->video_frame_cache(), - frame, - hash, - true)); - } + FramePtr frame = watcher->Get().value(); + RenderTicketWatcher* w = new RenderTicketWatcher(); + w->setProperty("job", QVariant::fromValue(last_update_time_)); + w->setProperty("frame", QVariant::fromValue(frame)); + video_download_tasks_.insert(w, hash); + connect(w, &RenderTicketWatcher::Finished, this, &PreviewAutoCacher::VideoDownloaded); + w->SetTicket(RenderManager::instance()->SaveFrameToCache(viewer_node_->video_frame_cache(), + frame, + true)); + */ } // Continue rendering @@ -508,13 +440,6 @@ void PreviewAutoCacher::SetPlayhead(const rational &playhead) RequeueFrames(); } -void PreviewAutoCacher::WaitForHashesToFinish() -{ - for (auto it=hash_tasks_.cbegin(); it!=hash_tasks_.cend(); it++) { - (*it)->waitForFinished(); - } -} - void PreviewAutoCacher::WaitForVideoDownloadsToFinish() { for (auto it=video_download_tasks_.cbegin(); it!=video_download_tasks_.cend(); it++) { @@ -598,8 +523,7 @@ void PreviewAutoCacher::TryRender() // Check if we have jobs running in other threads that shouldn't be interrupted right now // NOTE: We don't check for downloads because, while they run in another thread, they don't // require any access to the graph and therefore don't risk race conditions. - if (!hash_tasks_.isEmpty() - || !audio_tasks_.isEmpty() + if (!audio_tasks_.isEmpty() || !video_tasks_.isEmpty()) { return; } @@ -649,32 +573,6 @@ void PreviewAutoCacher::TryRender() // Ensure we are running tasks if we have any const int max_tasks = RenderManager::GetNumberOfIdealConcurrentJobs(); - // Handle hash tasks - while (hash_tasks_.size() < max_tasks && hash_iterator_.HasNext()) { - // Magic number: dunno what the best number for this is yet - static const int kMaxFrames = 1000; - - QVector times(kMaxFrames); - for (int i=0; i >* watcher = new QFutureWatcher< QVector >(); - watcher->setProperty("job", QVariant::fromValue(last_update_time_)); - hash_tasks_.append(watcher); - connect(watcher, &QFutureWatcher< QVector >::finished, this, &PreviewAutoCacher::HashesProcessed); - watcher->setFuture(QtConcurrent::run(PreviewAutoCacher::GenerateHashes, - copied_viewer_node_, - viewer_node_->video_frame_cache(), - times)); - } - // Handle video tasks rational t; while (video_tasks_.size() < max_tasks && queued_frame_iterator_.GetNext(&t)) { @@ -747,7 +645,6 @@ void PreviewAutoCacher::RequeueFrames() if (viewer_node_ && (viewer_node_->GetVideoAutoCacheEnabled() || use_custom_range_) && viewer_node_->video_frame_cache()->HasInvalidatedRanges(viewer_node_->GetVideoLength()) - && hash_tasks_.isEmpty() && !IsRenderingCustomRange()) { TimeRange using_range = use_custom_range_ ? custom_autocache_range_ : cache_range_; @@ -834,15 +731,6 @@ void PreviewAutoCacher::SetViewerNode(ViewerOutput *viewer_node) // Stop requeue timer if it's running delayed_requeue_timer_.stop(); - // Handle hashes - if (!hash_tasks_.isEmpty()) { - // Wait for hashes to finish - WaitForHashesToFinish(); - - // Clear the hash list to indicate we're not interested in the results of any of these - hash_tasks_.clear(); - } - // Handle video rendering tasks if (!video_tasks_.isEmpty()) { // Cancel any video tasks and wait for them to finish diff --git a/app/render/previewautocacher.h b/app/render/previewautocacher.h index f770c776f..39d1db77d 100644 --- a/app/render/previewautocacher.h +++ b/app/render/previewautocacher.h @@ -72,16 +72,6 @@ public: */ void SetPlayhead(const rational& playhead); - /** - * @brief If any hashes are currently running, wait for them to finish - * - * Once this function returns, it can be guaranteed that all hash tasks have been finished. - * They will NOT have been removed from the hash task list yet until they run HashesProcessed. - * If you don't want the continued processing in HashesProcessed to run, remove the task manually - * from the list after calling this function. It will still call HashesProcessed, but will be - * largely ignored (that function will simply free it). - */ - void WaitForHashesToFinish(); void WaitForVideoDownloadsToFinish(); /** @@ -142,14 +132,6 @@ private: void StartCachingVideoRange(const TimeRange &range); void StartCachingAudioRange(const TimeRange &range); - struct HashData { - rational time; - QByteArray hash; - bool exists; - }; - - static QVector GenerateHashes(ViewerOutput *viewer, FrameHashCache* cache, const QVector ×); - class QueuedJob { public: enum Type { @@ -190,7 +172,6 @@ private: RenderTicketPtr single_frame_render_; - QList >*> hash_tasks_; QMap audio_tasks_; QMap video_tasks_; QMap video_download_tasks_; @@ -225,11 +206,6 @@ private slots: */ void AudioInvalidated(const olive::TimeRange &range); - /** - * @brief Handler for when we have applied all the hashes to the FrameHashCache - */ - void HashesProcessed(); - /** * @brief Handler for when the RenderManager has returned rendered audio */ diff --git a/app/render/rendermanager.cpp b/app/render/rendermanager.cpp index ac5d297de..f34ab44a7 100644 --- a/app/render/rendermanager.cpp +++ b/app/render/rendermanager.cpp @@ -26,7 +26,6 @@ #include "config/config.h" #include "core.h" -#include "node/hashtraverser.h" #include "render/opengl/openglrenderer.h" #include "render/rendererthreadwrapper.h" #include "renderprocessor.h" @@ -100,19 +99,6 @@ void RenderManager::ClearOldDecoders() } } -QByteArray RenderManager::Hash(const Node *n, const Node::ValueHint &output, const VideoParams ¶ms, const rational &time) -{ - Q_ASSERT(n); - - if (n) { - HashTraverser hasher; - return hasher.GetHash(n, output, params, TimeRange(time, time + params.frame_rate_as_time_base())); - } else { - qCritical() << "Hash called with null node"; - return QByteArray(); - } -} - RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, ColorManager* color_manager, const rational& time, RenderMode::Mode mode, FrameHashCache* cache, bool prioritize, bool texture_only) @@ -201,14 +187,13 @@ RenderTicketPtr RenderManager::RenderAudio(ViewerOutput* viewer, const TimeRange return ticket; } -RenderTicketPtr RenderManager::SaveFrameToCache(FrameHashCache *cache, FramePtr frame, const QByteArray &hash, bool prioritize) +RenderTicketPtr RenderManager::SaveFrameToCache(FrameHashCache *cache, FramePtr frame, bool prioritize) { // Create ticket RenderTicketPtr ticket = std::make_shared(); ticket->setProperty("cache", cache->GetCacheDirectory()); ticket->setProperty("frame", QVariant::fromValue(frame)); - ticket->setProperty("hash", hash); ticket->setProperty("type", kTypeVideoDownload); if (ticket->thread() != this->thread()) { diff --git a/app/render/rendermanager.h b/app/render/rendermanager.h index df8c00d29..773389e60 100644 --- a/app/render/rendermanager.h +++ b/app/render/rendermanager.h @@ -63,11 +63,6 @@ public: return instance_; } - /** - * @brief Generate a unique identifier for a certain node at a cconst Node *n, const Node::ValueHint &outputertain time - */ - static QByteArray Hash(const Node *n, const Node::ValueHint &output, const VideoParams ¶ms, const rational &time); - /** * @brief Asynchronously generate a frame at a given time * @@ -103,7 +98,7 @@ public: RenderTicketPtr RenderAudio(ViewerOutput* viewer, const TimeRange& r, const AudioParams& params, RenderMode::Mode mode, bool generate_waveforms, bool prioritize = false); RenderTicketPtr RenderAudio(ViewerOutput *viewer, const TimeRange& r, RenderMode::Mode mode, bool generate_waveforms, bool prioritize = false); - RenderTicketPtr SaveFrameToCache(FrameHashCache* cache, FramePtr frame, const QByteArray& hash, bool prioritize = false); + RenderTicketPtr SaveFrameToCache(FrameHashCache* cache, FramePtr frame, bool prioritize = false); virtual void RunTicket(RenderTicketPtr ticket) const override; diff --git a/app/task/export/export.cpp b/app/task/export/export.cpp index 21c330c29..6d979fd69 100644 --- a/app/task/export/export.cpp +++ b/app/task/export/export.cpp @@ -156,20 +156,16 @@ bool ExportTask::Run() return success; } -bool ExportTask::FrameDownloaded(FramePtr f, const QByteArray &hash, const QVector ×) +bool ExportTask::FrameDownloaded(FramePtr f, const rational &time) { - Q_UNUSED(hash) + rational actual_time = time; - foreach (const rational& t, times) { - rational actual_time = t; - - if (params_.has_custom_range()) { - actual_time -= params_.custom_range().in(); - } - - time_map_.insert(actual_time, f); + if (params_.has_custom_range()) { + actual_time -= params_.custom_range().in(); } + time_map_.insert(actual_time, f); + while (!IsCancelled()) { rational real_time = Timecode::timestamp_to_time(frame_time_, video_params().frame_rate_as_time_base()); diff --git a/app/task/export/export.h b/app/task/export/export.h index 2ab27f917..169ef171c 100644 --- a/app/task/export/export.h +++ b/app/task/export/export.h @@ -38,7 +38,7 @@ public: protected: virtual bool Run() override; - virtual bool FrameDownloaded(FramePtr frame, const QByteArray& hash, const QVector& times) override; + virtual bool FrameDownloaded(FramePtr frame, const rational &time) override; virtual bool AudioDownloaded(const TimeRange& range, const SampleBuffer &samples) override; diff --git a/app/task/precache/precachetask.cpp b/app/task/precache/precachetask.cpp index 71cedb900..71560714e 100644 --- a/app/task/precache/precachetask.cpp +++ b/app/task/precache/precachetask.cpp @@ -85,14 +85,13 @@ bool PreCacheTask::Run() return true; } -bool PreCacheTask::FrameDownloaded(FramePtr frame, const QByteArray &hash, const QVector ×) +bool PreCacheTask::FrameDownloaded(FramePtr frame, const rational &time) { // Do nothing. Pre-cache essentially just creates more frames in the cache, it doesn't need to do // anything else. Q_UNUSED(frame) - Q_UNUSED(hash) - Q_UNUSED(times) + Q_UNUSED(time) return true; } diff --git a/app/task/precache/precachetask.h b/app/task/precache/precachetask.h index dcd9369bf..3f3de1964 100644 --- a/app/task/precache/precachetask.h +++ b/app/task/precache/precachetask.h @@ -38,7 +38,7 @@ public: protected: virtual bool Run() override; - virtual bool FrameDownloaded(FramePtr frame, const QByteArray& hash, const QVector& times) override; + virtual bool FrameDownloaded(FramePtr frame, const rational ×) override; virtual bool AudioDownloaded(const TimeRange& range, const SampleBuffer &samples) override; diff --git a/app/task/render/render.cpp b/app/task/render/render.cpp index 32b214e8b..c9b30448a 100644 --- a/app/task/render/render.cpp +++ b/app/task/render/render.cpp @@ -74,58 +74,17 @@ bool RenderTask::Render(ColorManager* manager, } // Look up hashes - QMap > time_map; - QVector > frame_render_order; - - if (!video_range.isEmpty() && viewer()->GetConnectedTextureOutput()) { - // Get list of discrete frames from range - TimeRangeListFrameIterator iterator(video_range, video_params().frame_rate_as_time_base()); - QVector times(iterator.size()); - QVector hashes(iterator.size()); - - // Generate hashes - rational r; - for (int i=0; iterator.GetNext(&r); i++) { - if (IsCancelled()) { - break; - } - - times[i] = r; - hashes[i] = RenderManager::instance()->Hash(viewer()->GetConnectedTextureOutput(), viewer()->GetConnectedTextureValueHint(), video_params_, r); - } - - // Filter out duplicates - for (int i=0; i& hash_time_list = time_map[hash]; - hash_time_list.append(times.at(i)); - - if (hash_time_list.size() == 1) { - frame_render_order.append({times.at(i), hash}); - } - } - - // Add to "total progress" - total_number_of_frames_ = times.size(); - total_number_of_unique_frames_ = time_map.size(); - total_length += total_number_of_unique_frames_; - } + TimeRangeListFrameIterator iterator(video_range, video_params().frame_rate_as_time_base()); // Start a render of a limited amount, and then render one frame for each frame that gets // finished. This prevents rendered frames from stacking up in memory indefinitely while the // encoder is processing them. The amount is kind of arbitrary, but we use the thread count so // each of the system's threads are utilized as memory allows. const int maximum_rendered_frames = QThread::idealThreadCount(); - auto frame_iterator = frame_render_order.cbegin(); - for (int i=0; isecond, &watcher_thread, manager, frame_iterator->first, - mode, cache, force_size, force_matrix, force_format, force_color_output); + rational next_frame; + for (int i=0; iGet().value(), - watcher->property("hash").toByteArray())) { + if (!DownloadFrame(&watcher_thread, watcher->Get().value())) { result = false; } @@ -220,8 +177,7 @@ bool RenderTask::Render(ColorManager* manager, } else { // Assume single-step video or video download ticket - QByteArray rendered_hash = watcher->property("hash").toByteArray(); - if (!FrameDownloaded(watcher->Get().value(), rendered_hash, time_map.value(rendered_hash))) { + if (!FrameDownloaded(watcher->Get().value(), watcher->property("time").value())) { result = false; } @@ -235,11 +191,8 @@ bool RenderTask::Render(ColorManager* manager, emit ProgressChanged(progress_counter / total_length); } - if (frame_iterator != frame_render_order.cend()) { - StartTicket(frame_iterator->second, &watcher_thread, manager, frame_iterator->first, - mode, cache, force_size, force_matrix, force_format, force_color_output); - - frame_iterator++; + if (iterator.GetNext(&next_frame)) { + StartTicket(&watcher_thread, manager, next_frame, mode, cache, force_size, force_matrix, force_format, force_color_output); } } @@ -284,17 +237,14 @@ bool RenderTask::Render(ColorManager* manager, return result; } -bool RenderTask::DownloadFrame(QThread *thread, FramePtr frame, const QByteArray &hash) +bool RenderTask::DownloadFrame(QThread *thread, FramePtr frame) { RenderTicketWatcher* watcher = new RenderTicketWatcher(); - watcher->setProperty("hash", hash); PrepareWatcher(watcher, thread); IncrementRunningTickets(); - watcher->SetTicket(RenderManager::instance()->SaveFrameToCache(viewer_->video_frame_cache(), - frame, - hash)); + watcher->SetTicket(RenderManager::instance()->SaveFrameToCache(viewer_->video_frame_cache(), frame)); // NOTE: Doesn't reflect the actual return result of SaveFrameToCache return true; @@ -320,17 +270,15 @@ void RenderTask::IncrementRunningTickets() finished_watcher_mutex_.unlock(); } -void RenderTask::StartTicket(const QByteArray& hash, QThread* watcher_thread, ColorManager* manager, +void RenderTask::StartTicket(QThread* watcher_thread, ColorManager* manager, const rational& time, RenderMode::Mode mode, FrameHashCache* cache, const QSize &force_size, const QMatrix4x4 &force_matrix, VideoParams::Format force_format, ColorProcessorPtr force_color_output) { RenderTicketWatcher* watcher = new RenderTicketWatcher(); - watcher->setProperty("hash", hash); + watcher->setProperty("time", QVariant::fromValue(time)); PrepareWatcher(watcher, watcher_thread); - IncrementRunningTickets(); - watcher->SetTicket(RenderManager::instance()->RenderFrame(viewer_, manager, time, mode, video_params_, audio_params_, force_size, force_matrix, diff --git a/app/task/render/render.h b/app/task/render/render.h index 01909081f..ef8f4807a 100644 --- a/app/task/render/render.h +++ b/app/task/render/render.h @@ -49,9 +49,9 @@ protected: VideoParams::Format force_format = VideoParams::kFormatInvalid, ColorProcessorPtr force_color_output = nullptr); - virtual bool DownloadFrame(QThread* thread, FramePtr frame, const QByteArray &hash); + virtual bool DownloadFrame(QThread* thread, FramePtr frame); - virtual bool FrameDownloaded(FramePtr frame, const QByteArray& hash, const QVector& times) = 0; + virtual bool FrameDownloaded(FramePtr frame, const rational &time) = 0; virtual bool AudioDownloaded(const TimeRange& range, const SampleBuffer &samples) = 0; @@ -125,7 +125,7 @@ private: void IncrementRunningTickets(); - void StartTicket(const QByteArray &hash, QThread *watcher_thread, ColorManager *manager, const rational &time, RenderMode::Mode mode, FrameHashCache *cache, const QSize &force_size, const QMatrix4x4 &force_matrix, VideoParams::Format force_format, ColorProcessorPtr force_color_output); + void StartTicket(QThread *watcher_thread, ColorManager *manager, const rational &time, RenderMode::Mode mode, FrameHashCache *cache, const QSize &force_size, const QMatrix4x4 &force_matrix, VideoParams::Format force_format, ColorProcessorPtr force_color_output); ViewerOutput* viewer_;