From 6a4f3f613c9cf469e62b5149e0b7c13a28ccd878 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 16 Jun 2020 03:07:53 +1000 Subject: [PATCH] rendertask: remove sorting code and calculate duplicate hashes while retrieving These are two changes that speed up when frames start getting rendered and processed (particularly for larger scale projects): * Sorting of time beforehand was done to optimize decoding but is actually unnecessary since the frames are already sequential when they're retrieved from the range. As long as we maintain that order, no sorting is required. This speeds up caching IMMENSELY. * Evaluate hashes concurrently while retrieving frames. Before, this was done before any rendering and could therefore delay rendering by several seconds. Doing them concurrently means frames start rendering almost immediately and further hashes can be analyzed while we wait for frames to finish rendering. --- app/task/render/render.cpp | 134 ++++++++++++++++--------------------- 1 file changed, 59 insertions(+), 75 deletions(-) diff --git a/app/task/render/render.cpp b/app/task/render/render.cpp index d46d1d778..175e3144d 100644 --- a/app/task/render/render.cpp +++ b/app/task/render/render.cpp @@ -87,89 +87,25 @@ void RenderTask::Render(const TimeRangeList& video_range, } } - QMap times_to_render; - std::list render_lookup_table; - QList times; - QList hashes; + QVector times; + QVector hashes; + std::list frame_queue; if (!video_range.isEmpty()) { + QList existing_hashes; - { - QList existing_hashes; - - foreach (const TimeRange& r, video_range) { - total_length += r.length().toDouble(); - } - - times = viewer_->video_frame_cache()->GetFrameListFromTimeRange(video_range); - - QFuture > hash_future = backend_.Hash(times); - hashes = hash_future.result(); - - // Determine any duplicates - for (int index=0;indexvideo_frame_cache()->CachePathName(hash)); - - if (hash_exists) { - existing_hashes.append(hash); - } - } - - if (hash_exists) { - // Already exists, no need to render it again - FrameDownloaded(hash, {time}); - progress_counter += video_frame_sz; - emit ProgressChanged(progress_counter / total_length); - } - } - - if (!hash_exists && (!map_contains_hash || times_to_render.value(hash) > time)) { - times_to_render.insert(hash, time); - } - } + foreach (const TimeRange& r, video_range) { + total_length += r.length().toDouble(); } - // Render all frames necessary - { - QMap::const_iterator i; - std::list::iterator j; + times = viewer_->video_frame_cache()->GetFrameListFromTimeRange(video_range); - std::list sorted_times; + QFuture > hash_future = backend_.Hash(times); + hashes = hash_future.result(); - // Rendering is generally more efficient when done sequentially, so we sort the - // times chronologically here - for (i=times_to_render.begin(); i!=times_to_render.end(); i++) { - bool inserted = false; - - for (j=sorted_times.begin(); j!=sorted_times.end(); j++) { - if (j->time > i.value()) { - sorted_times.insert(j, {i.value(), i.key()});\ - inserted = true; - break; - } - } - - if (!inserted) { - sorted_times.push_back({i.value(), i.key()}); - } - } - - // Start render jobs in sorted order - for (j=sorted_times.begin(); j!=sorted_times.end(); j++) { - render_lookup_table.push_back({j->hash, backend_.RenderFrame(j->time)}); - } + for (int i=0;i::iterator j; std::list::iterator k; + std::list running_hashes; + std::list existing_hashes; + while (!IsCancelled() && (!render_lookup_table.empty() + || !frame_queue.empty() || !download_futures.empty() || !audio_lookup_table.empty())) { + if (!frame_queue.empty()) { + // Pop another frame off the frame queue + const HashTimePair& p = frame_queue.front(); + + // Check if we're already rendering this hash + bool rendering_hash = (std::find(running_hashes.begin(), running_hashes.end(), p.hash) != running_hashes.end()); + + // Skip this hash if we're already rendering it + if (!rendering_hash) { + // Check if this frame already exists (has already been rendered previously or during this job) + bool hash_exists = false; + + if (use_disk_cache) { + bool hash_exists = (std::find(existing_hashes.begin(), existing_hashes.end(), p.hash) != existing_hashes.end()); + + if (!hash_exists) { + hash_exists = QFileInfo::exists(viewer_->video_frame_cache()->CachePathName(p.hash)); + + if (hash_exists) { + existing_hashes.push_back(p.hash); + } + } + + if (hash_exists) { + // Already exists, no need to render it again + FrameDownloaded(p.hash, {p.time}); + progress_counter += video_frame_sz; + emit ProgressChanged(progress_counter / total_length); + } + } + + // If no existing disk cache was found, queue it now + if (!hash_exists) { + render_lookup_table.push_back({p.hash, backend_.RenderFrame(p.time)}); + running_hashes.push_back(p.hash); + } + } + + // Remove first element + frame_queue.pop_front(); + } + i = render_lookup_table.begin(); while (!IsCancelled() && i != render_lookup_table.end()) { @@ -216,6 +198,8 @@ void RenderTask::Render(const TimeRangeList& video_range, FrameDownloaded(j->hash, times_with_hash); + existing_hashes.push_back(j->hash); + // Signal process progress_counter += times_with_hash.size() * video_frame_sz; emit ProgressChanged(progress_counter / total_length);