use a system of deferred maps to keep track of frames that don't need to be rendered twice

Mostly used for exporting to ensure all frames get accounted for when being
sent from the renderer to the exporter through signals/slots.
This commit is contained in:
itsmattkc
2019-12-22 03:03:08 +11:00
parent 8514590c83
commit 46855adea7
5 changed files with 39 additions and 29 deletions
@@ -45,8 +45,6 @@ bool OpenGLWorker::InitInternal()
ctx_->moveToThread(this->thread());
//qDebug() << "Processor initialized in thread" << thread() << "- context is in" << ctx_->thread();
// The rest of the initialization needs to occur in the other thread, so we signal for it to start
QMetaObject::invokeMethod(this, "FinishInit", Qt::QueuedConnection);
+19 -19
View File
@@ -27,6 +27,7 @@
#include <QDir>
#include <QtMath>
#include "common/timecodefunctions.h"
#include "render/pixelservice.h"
#include "videorenderworker.h"
@@ -55,12 +56,10 @@ void VideoRenderBackend::InvalidateCache(const rational &start_range, const rati
<< end_range_adj.toDouble();
// Snap start_range to timebase
double start_range_dbl = start_range_adj.toDouble();
double start_range_numf = start_range_dbl * static_cast<double>(params_.time_base().denominator());
int64_t start_range_numround = qFloor(start_range_numf/static_cast<double>(params_.time_base().numerator())) * params_.time_base().numerator();
rational true_start_range(start_range_numround, params_.time_base().denominator());
int64_t timestamp = olive::time_to_timestamp(start_range_adj, params_.time_base());
rational true_start = olive::timestamp_to_time(timestamp, params_.time_base());
for (rational r=true_start_range;r<=end_range_adj;r+=params_.time_base()) {
for (rational r=true_start;r<end_range_adj;r+=params_.time_base()) {
// Try to order the queue from closest to the playhead to furthest
rational last_time = last_time_requested_;
@@ -248,6 +247,18 @@ void VideoRenderBackend::ThreadCompletedFrame(NodeDependency path, QByteArray ha
QVariant texture = table.Get(NodeParam::kTexture);
// Check if this frame has changed once again, in which case we may not want to draw it (it'll look jittery to the user)
if (!TimeIsQueued(TimeRange(path.in(), path.in()))) {
EmitCachedFrameReady(path.in(), texture);
if (export_mode_) {
QList<rational> times_with_this_hash = frame_cache()->DeferredMapsWithHash(hash);
foreach (const rational& t, times_with_this_hash) {
EmitCachedFrameReady(t, texture);
}
}
}
if (!export_mode_) {
if (texture.isNull()) {
// No frame received, we set hash to an empty
@@ -268,18 +279,6 @@ void VideoRenderBackend::ThreadCompletedFrame(NodeDependency path, QByteArray ha
}
}
// Check if this frame has changed once again, in which case we may not want to draw it (it'll look jittery to the user)
if (!TimeIsQueued(TimeRange(path.in(), path.in()))) {
EmitCachedFrameReady(path.in(), texture);
if (export_mode_) {
QList<rational> times_with_this_hash = frame_cache()->TimesWithHash(hash);
foreach (const rational& t, times_with_this_hash) {
EmitCachedFrameReady(t, texture);
}
}
}
// Queue up a new frame for this worker
CacheNext();
}
@@ -287,17 +286,18 @@ void VideoRenderBackend::ThreadCompletedFrame(NodeDependency path, QByteArray ha
void VideoRenderBackend::ThreadCompletedDownload(NodeDependency dep, QByteArray hash)
{
frame_cache()->SetHash(dep.in(), hash);
emit CachedTimeReady(dep.in());
// Emit for each frame that has this hash (some may have been added in ThreadSkippedFrame)
QList<rational> times_with_this_hash = frame_cache()->TimesWithHash(hash);
QList<rational> times_with_this_hash = frame_cache()->DeferredMapsWithHash(hash);
foreach (const rational& t, times_with_this_hash) {
frame_cache()->SetHash(t, hash);
emit CachedTimeReady(t);
}
}
void VideoRenderBackend::ThreadSkippedFrame(NodeDependency dep, QByteArray hash)
{
frame_cache()->SetHash(dep.in(), hash);
SetWorkerBusyState(static_cast<RenderWorker*>(sender()), false);
// Queue up a new frame for this worker
+16 -5
View File
@@ -26,13 +26,15 @@ bool VideoRenderFrameCache::IsCaching(const QByteArray &hash)
return is_caching;
}
bool VideoRenderFrameCache::TryCache(const QByteArray &hash)
bool VideoRenderFrameCache::TryCache(const rational& time, const QByteArray &hash)
{
currently_caching_lock_.lock();
bool is_caching = currently_caching_list_.contains(hash);
if (!is_caching) {
if (is_caching) {
deferred_maps_.insert(time, hash);
} else {
currently_caching_list_.append(hash);
}
@@ -80,18 +82,27 @@ void VideoRenderFrameCache::Truncate(const rational &time)
}
}
QList<rational> VideoRenderFrameCache::TimesWithHash(const QByteArray &hash)
QList<rational> VideoRenderFrameCache::DeferredMapsWithHash(const QByteArray &hash)
{
QList<rational> list;
QMap<rational, QByteArray>::const_iterator iterator;
currently_caching_lock_.lock();
for (iterator=time_hash_map_.begin();iterator!=time_hash_map_.end();iterator++) {
QMap<rational, QByteArray>::iterator iterator = deferred_maps_.begin();
while (iterator != deferred_maps_.end()) {
if (iterator.value() == hash) {
list.append(iterator.key());
iterator = deferred_maps_.erase(iterator);
} else {
iterator++;
}
}
currently_caching_list_.removeOne(hash);
currently_caching_lock_.unlock();
return list;
}
+3 -2
View File
@@ -23,7 +23,7 @@ public:
/**
* @brief Check if a frame is currently being cached, and if not reserve it
*/
bool TryCache(const QByteArray& hash);
bool TryCache(const rational &time, const QByteArray& hash);
/**
* @brief Return the path of the cached image at this time
@@ -39,12 +39,13 @@ public:
void Truncate(const rational& time);
QList<rational> TimesWithHash(const QByteArray& hash);
QList<rational> DeferredMapsWithHash(const QByteArray& hash);
private:
void RemoveHashFromCurrentlyCaching(const QByteArray& hash);
QMap<rational, QByteArray> time_hash_map_;
QMap<rational, QByteArray> deferred_maps_;
QMutex currently_caching_lock_;
QVector<QByteArray> currently_caching_list_;
+1 -1
View File
@@ -30,7 +30,7 @@ NodeValueTable VideoRenderWorker::RenderInternal(const NodeDependency& path)
if (frame_cache_->HasHash(hash)) {
// We've already cached this hash, no need to continue
emit HashAlreadyExists(path, hash);
} else if (frame_cache_->TryCache(hash)) {
} else if (frame_cache_->TryCache(path.in(), hash)) {
// This hash is available for us to cache, start traversing graph
value = ProcessNode(path);