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:
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user