Skip to content

Commit

Permalink
nodes: implemented infrastructure for caching any node
Browse files Browse the repository at this point in the history
Not accessible through UI yet, but ported Footage node to it
(previously we used a hack to ensure Footage textures were
cached, now it's a formal infrastructure).
  • Loading branch information
itsmattkc committed Apr 11, 2021
1 parent 210eb86 commit 6c30f8c
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 39 deletions.
3 changes: 2 additions & 1 deletion app/node/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ Node::Node(bool create_default_output) :
override_color_(-1),
last_change_time_(0),
folder_(nullptr),
operation_stack_(0)
operation_stack_(0),
cache_result_(false)
{
if (create_default_output) {
AddOutput();
Expand Down
12 changes: 12 additions & 0 deletions app/node/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,16 @@ class Node : public QObject
folder_ = folder;
}

bool GetCacheTextures() const
{
return cache_result_;
}

void SetCacheTextures(bool e)
{
cache_result_ = e;
}

static const QString kDefaultOutput;

protected:
Expand Down Expand Up @@ -1191,6 +1201,8 @@ class Node : public QObject

int operation_stack_;

bool cache_result_;

private slots:
/**
* @brief Slot when a keyframe's time changes to keep the keyframes correctly sorted by time
Expand Down
2 changes: 2 additions & 0 deletions app/node/project/footage/footage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ Footage::Footage(const QString &filename) :
Clear();

set_filename(filename);

SetCacheTextures(true);
}

void Footage::Retranslate()
Expand Down
37 changes: 27 additions & 10 deletions app/node/traverser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include "node.h"
#include "render/job/footagejob.h"
#include "render/rendermanager.h"

namespace olive {

Expand Down Expand Up @@ -110,7 +111,7 @@ NodeValueTable NodeTraverser::GenerateTable(const Node *n, const QString& output
// By this point, the node should have all the inputs it needs to render correctly
NodeValueTable table = n->Value(output, database);

PostProcessTable(n, range, table);
PostProcessTable(n, output, range, table);

return table;
}
Expand Down Expand Up @@ -171,10 +172,15 @@ QVariant NodeTraverser::ProcessFrameGeneration(const Node *node, const GenerateJ
return QVariant();
}

QVariant NodeTraverser::GetCachedFrame(const Node *node, const rational &time)
void NodeTraverser::SaveCachedTexture(const QByteArray &hash, const QVariant &texture)
{
Q_UNUSED(node)
Q_UNUSED(time)
Q_UNUSED(hash)
Q_UNUSED(texture)
}

QVariant NodeTraverser::GetCachedTexture(const QByteArray& hash)
{
Q_UNUSED(hash)

return QVariant();
}
Expand All @@ -190,17 +196,23 @@ void NodeTraverser::AddGlobalsToDatabase(NodeValueDatabase &db, const TimeRange&
db.Insert(QStringLiteral("global"), global);
}

void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, NodeValueTable &output_params)
void NodeTraverser::PostProcessTable(const Node *node, const QString& output, const TimeRange &range, NodeValueTable &output_params)
{
bool got_cached_frame = false;
QByteArray cached_node_hash;

// Convert footage to image/sample buffers
QVariant cached_frame = GetCachedFrame(node, range.in());
if (!cached_frame.isNull()) {
output_params.Push(NodeValue::kTexture, cached_frame, node);
if (CanCacheFrames() && node->GetCacheTextures()) {
// This node is set to cache the result, see if we can retrieved a previously cached version
cached_node_hash = RenderManager::Hash(node, output, GetCacheVideoParams(), range.in());

// No more to do here
got_cached_frame = true;
QVariant cached_frame = GetCachedTexture(cached_node_hash);
if (!cached_frame.isNull()) {
output_params.Push(NodeValue::kTexture, cached_frame, node);

// No more to do here
got_cached_frame = true;
}
}

// Strip out any jobs or footage
Expand Down Expand Up @@ -285,6 +297,11 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N
output_params.Push(NodeValue::kSamples, value, node);
}
}

if (CanCacheFrames() && node->GetCacheTextures() && !got_cached_frame) {
// Save cached texture
SaveCachedTexture(cached_node_hash, output_params.Get(NodeValue::kTexture));
}
}

}
16 changes: 14 additions & 2 deletions app/node/traverser.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,19 @@ class NodeTraverser : public CancelableObject

virtual QVariant ProcessFrameGeneration(const Node *node, const GenerateJob& job);

virtual QVariant GetCachedFrame(const Node *node, const rational &time);
virtual QVariant GetCachedTexture(const QByteArray& hash);

virtual void SaveCachedTexture(const QByteArray& hash, const QVariant& texture);

virtual bool CanCacheFrames()
{
return false;
}

virtual VideoParams GetCacheVideoParams()
{
return VideoParams();
}

void AddGlobalsToDatabase(NodeValueDatabase& db, const TimeRange &range) const;

Expand All @@ -69,7 +81,7 @@ class NodeTraverser : public CancelableObject
}

private:
void PostProcessTable(const Node *node, const TimeRange &range, NodeValueTable &output_params);
void PostProcessTable(const Node *node, const QString &output, const TimeRange &range, NodeValueTable &output_params);

};

Expand Down
20 changes: 15 additions & 5 deletions app/render/framehashcache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,14 +187,24 @@ bool FrameHashCache::SaveCacheFrame(const QByteArray& hash,
const VideoParams& vparam,
int linesize_bytes) const
{
QString fn = CachePathName(hash);
return SaveCacheFrame(GetCacheDirectory(), hash, data, vparam, linesize_bytes);
}

bool FrameHashCache::SaveCacheFrame(const QByteArray &hash, FramePtr frame) const
{
return SaveCacheFrame(GetCacheDirectory(), hash, frame);
}

bool FrameHashCache::SaveCacheFrame(const QString &cache_path, const QByteArray &hash, char *data, const VideoParams &vparam, int linesize_bytes)
{
QString fn = CachePathName(cache_path, hash);

if (SaveCacheFrame(fn, data, vparam, linesize_bytes)) {
// Register frame with the disk manager
QMetaObject::invokeMethod(DiskManager::instance(),
"CreatedFile",
Qt::QueuedConnection,
Q_ARG(QString, GetCacheDirectory()),
Q_ARG(QString, cache_path),
Q_ARG(QString, fn),
Q_ARG(QByteArray, hash));

Expand All @@ -204,10 +214,10 @@ bool FrameHashCache::SaveCacheFrame(const QByteArray& hash,
}
}

bool FrameHashCache::SaveCacheFrame(const QByteArray &hash, FramePtr frame) const
bool FrameHashCache::SaveCacheFrame(const QString &cache_path, const QByteArray &hash, FramePtr frame)
{
if (frame) {
return SaveCacheFrame(hash, frame->data(), frame->video_params(), frame->linesize_bytes());
return SaveCacheFrame(cache_path, hash, frame->data(), frame->video_params(), frame->linesize_bytes());
} else {
qWarning() << "Attempted to save a NULL frame to the cache. This may or may not be desirable.";
return false;
Expand Down Expand Up @@ -394,7 +404,7 @@ QString FrameHashCache::CachePathName(const QString &cache_path, const QByteArra
return cache_dir.filePath(filename);
}

bool FrameHashCache::SaveCacheFrame(const QString &filename, char *data, const VideoParams &vparam, int linesize_bytes) const
bool FrameHashCache::SaveCacheFrame(const QString &filename, char *data, const VideoParams &vparam, int linesize_bytes)
{
if (!VideoParams::FormatIsFloat(vparam.format())) {
qCritical() << "Tried to cache frame with non-float pixel format";
Expand Down
4 changes: 3 additions & 1 deletion app/render/framehashcache.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,11 @@ class FrameHashCache : public PlaybackCache
QString CachePathName(const QByteArray &hash) const;
static QString CachePathName(const QString& cache_path, const QByteArray &hash);

bool SaveCacheFrame(const QString& filename, char *data, const VideoParams &vparam, int linesize_bytes) const;
static bool SaveCacheFrame(const QString& filename, char *data, const VideoParams &vparam, int linesize_bytes);
bool SaveCacheFrame(const QByteArray& hash, char *data, const VideoParams &vparam, int linesize_bytes) const;
bool SaveCacheFrame(const QByteArray& hash, FramePtr frame) const;
static bool SaveCacheFrame(const QString& cache_path, const QByteArray& hash, char *data, const VideoParams &vparam, int linesize_bytes);
static bool SaveCacheFrame(const QString& cache_path, const QByteArray& hash, FramePtr frame);
static FramePtr LoadCacheFrame(const QString& cache_path, const QByteArray& hash);
FramePtr LoadCacheFrame(const QByteArray& hash) const;
static FramePtr LoadCacheFrame(const QString& fn);
Expand Down
2 changes: 1 addition & 1 deletion app/render/rendermanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ RenderTicketPtr RenderManager::SaveFrameToCache(FrameHashCache *cache, FramePtr
// Create ticket
RenderTicketPtr ticket = std::make_shared<RenderTicket>();

ticket->setProperty("cache", Node::PtrToValue(cache));
ticket->setProperty("cache", cache->GetCacheDirectory());
ticket->setProperty("frame", QVariant::fromValue(frame));
ticket->setProperty("hash", hash);
ticket->setProperty("type", kTypeVideoDownload);
Expand Down
64 changes: 46 additions & 18 deletions app/render/renderprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,11 @@ void RenderProcessor::Run()
}
case RenderManager::kTypeVideoDownload:
{
FrameHashCache* cache = Node::ValueToPtr<FrameHashCache>(ticket_->property("cache"));
QString cache = ticket_->property("cache").toString();
FramePtr frame = ticket_->property("frame").value<FramePtr>();
QByteArray hash = ticket_->property("hash").toByteArray();

ticket_->Finish(cache->SaveCacheFrame(hash, frame), false);
ticket_->Finish(FrameHashCache::SaveCacheFrame(cache, hash, frame), false);
break;
}
default:
Expand Down Expand Up @@ -534,34 +534,62 @@ QVariant RenderProcessor::ProcessFrameGeneration(const Node *node, const Generat
return QVariant::fromValue(texture);
}

QVariant RenderProcessor::GetCachedFrame(const Node *node, const rational &time)
bool RenderProcessor::CanCacheFrames()
{
if (!ticket_->property("cache").toString().isEmpty()
&& node->id() == QStringLiteral("org.olivevideoeditor.Olive.videoinput")) {
const VideoParams& video_params = ticket_->property("vparam").value<VideoParams>();
return true;
}

QByteArray hash = RenderManager::Hash(node, video_params, time);
QVariant RenderProcessor::GetCachedTexture(const QByteArray& hash)
{
VideoParams video_params = GetCacheVideoParams();
QString cache_dir = ticket_->property("cache").toString();

FramePtr f = FrameHashCache::LoadCacheFrame(ticket_->property("cache").toString(), hash);
FramePtr f = FrameHashCache::LoadCacheFrame(cache_dir, hash);

if (f) {
// The cached frame won't load with the correct divider by default, so we enforce it here
VideoParams p = f->video_params();
if (f) {
// The cached frame won't load with the correct divider by default, so we enforce it here
VideoParams p = f->video_params();

p.set_width(f->width() * video_params.divider());
p.set_height(f->height() * video_params.divider());
p.set_divider(video_params.divider());
p.set_width(f->width() * video_params.divider());
p.set_height(f->height() * video_params.divider());
p.set_divider(video_params.divider());

f->set_video_params(p);
f->set_video_params(p);

TexturePtr texture = render_ctx_->CreateTexture(f->video_params(), f->data(), f->linesize_pixels());
return QVariant::fromValue(texture);
}
TexturePtr texture = render_ctx_->CreateTexture(f->video_params(), f->data(), f->linesize_pixels());
qDebug() << "Loaded mid-render frame from cache";
return QVariant::fromValue(texture);
}

return QVariant();
}

void RenderProcessor::SaveCachedTexture(const QByteArray &hash, const QVariant &tex_var)
{
// FIXME: Temporarily disabled because I don't know how to ensure that the frame saved here is
// not the main frame. If it is, it'll be saved twice which will waste a lot of cycles.
// At least disabled, the frame will still save, and if nothing else alters the hash, it
// will pick up automatically from GetCachedTexture.
/*if (!tex_var.isNull()) {
QString cache_dir = ticket_->property("cache").toString();
if (!cache_dir.isEmpty()) {
TexturePtr texture = tex_var.value<TexturePtr>();
FramePtr frame = Frame::Create();
frame->set_video_params(texture->params());
frame->allocate();
render_ctx_->DownloadFromTexture(texture.get(), frame->data(), frame->linesize_pixels());
FrameHashCache::SaveCacheFrame(cache_dir, hash, frame);
qDebug() << "Saved mid-render frame to cache";
}
}*/
}

VideoParams RenderProcessor::GetCacheVideoParams()
{
return ticket_->property("vparam").value<VideoParams>();
}

QVector2D RenderProcessor::GenerateResolution() const
{
// Set resolution to the destination to the "logical" resolution of the destination
Expand Down
8 changes: 7 additions & 1 deletion app/render/renderprocessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,13 @@ class RenderProcessor : public NodeTraverser

virtual QVariant ProcessFrameGeneration(const Node *node, const GenerateJob& job) override;

virtual QVariant GetCachedFrame(const Node *node, const rational &time) override;
virtual bool CanCacheFrames() override;

virtual QVariant GetCachedTexture(const QByteArray &hash) override;

virtual void SaveCachedTexture(const QByteArray& hash, const QVariant& texture) override;

virtual VideoParams GetCacheVideoParams() override;

virtual QVector2D GenerateResolution() const override;

Expand Down

0 comments on commit 6c30f8c

Please sign in to comment.