renderer: share render backend for auto-cache tasks

Since auto-cache events start and stop fairly frequently (and are somewhat
heavy to create/destroy), we can save a lot of cycles over time by sharing
the same backend between them all.
This commit is contained in:
itsmattkc
2020-07-06 14:22:17 +10:00
parent 936d077077
commit a45f514f68
8 changed files with 128 additions and 58 deletions
+19
View File
@@ -537,6 +537,25 @@ bool Node::OutputsTo(const QString &id, bool recursively) const
return false;
}
bool Node::OutputsTo(NodeInput *input, bool recursively) const
{
QList<NodeOutput*> outputs = GetOutputs();
foreach (NodeOutput* output, outputs) {
foreach (NodeEdgePtr edge, output->edges()) {
NodeInput* connected = edge->input();
if (connected == input) {
return true;
} else if (recursively && connected->parentNode()->OutputsTo(input, recursively)) {
return true;
}
}
}
return false;
}
bool Node::InputsFrom(Node *n, bool recursively) const
{
QList<NodeInput*> inputs = GetInputsIncludingArrays();
+5
View File
@@ -220,6 +220,11 @@ public:
*/
bool OutputsTo(const QString& id, bool recursively) const;
/**
* @brief Same as OutputsTo(Node*), but for a specific node input rather than just a node.
*/
bool OutputsTo(NodeInput* input, bool recursively) const;
/**
* @brief Returns whether this node ever receives an input from a particular node instance
*/
+27 -23
View File
@@ -195,27 +195,37 @@ std::list<TimeRange> RenderBackend::SplitRangeIntoChunks(const TimeRange &r)
void RenderBackend::NodeGraphChanged(NodeInput *source)
{
if (!graph_update_queue_.isEmpty()) {
// First, check if anything in our queue is a dependency of this input. If so, we should remove
// it and just update this input.
// We need to determine:
// - If we don't have this input, assume that it's coming soon and ignore it
// - If we do, is this input a child of another input we're already copying?
// - Or are any of the queued inputs children of this one?
// First we need to find our copy of the input being queued
Node* our_copy_node = copy_map_.value(source->parentNode());
// First we need to find our copy of the input being queued
Node* our_copy_node = copy_map_.value(source->parentNode());
if (our_copy_node) {
NodeInput* our_copy = our_copy_node->GetInputWithID(source->id());
QList<Node*> our_copy_deps = our_copy->GetDependencies(our_copy);
// If we don't have this node yet, assume it's coming in a later copy in which case it'll be
// copied then
if (!our_copy_node) {
// Assert that there are updates coming
Q_ASSERT(!graph_update_queue_.isEmpty());
return;
}
for (int i=0;i<graph_update_queue_.size();i++) {
NodeInput* check_input = graph_update_queue_.at(i);
Node* check_input_our_copy = copy_map_.value(check_input->parentNode());
// If we're here, we must have this node. Determine if we're already copying a "parent" of this
for (int i=0; i<graph_update_queue_.size(); i++) {
NodeInput* queued_input = graph_update_queue_.at(i);
// If this input isn't connected anymore, it obviously won't come up as a dependency
if (our_copy_deps.contains(check_input_our_copy)) {
graph_update_queue_.removeAt(i);
i--;
}
}
// Check if this dependency graph is already queued
if (source->parentNode()->OutputsTo(queued_input, true)) {
// In which case, no further copy is necessary
return;
}
// Check if this input supersedes an already queued input
if (queued_input->parentNode()->OutputsTo(source, true)) {
// In which case, we don't need to queue it and can queue our own
graph_update_queue_.removeAt(i);
i--;
}
}
@@ -329,15 +339,9 @@ void RenderBackend::RunNextJob()
void RenderBackend::ProcessUpdateQueue()
{
/*
while (!graph_update_queue_.isEmpty()) {
CopyNodeInputValue(graph_update_queue_.takeFirst());
}
*/
// FIXME: SLOW DEBUGGING CODE
CopyNodeInputValue(viewer_node_->texture_input());
CopyNodeInputValue(viewer_node_->samples_input());
}
QByteArray RenderBackend::HashNode(const Node *n, const VideoParams &params, const rational &time)
+15
View File
@@ -43,6 +43,11 @@ public:
void Close();
ViewerOutput* GetViewerNode() const
{
return viewer_node_;
}
void SetViewerNode(ViewerOutput* viewer_node);
void SetUpdateWithGraph(bool e)
@@ -81,6 +86,16 @@ public:
*/
RenderTicketPtr RenderAudio(const TimeRange& r);
const VideoParams& GetVideoParams() const
{
return video_params_;
}
const AudioParams& GetAudioParams() const
{
return audio_params_;
}
void SetVideoParams(const VideoParams& params);
void SetAudioParams(const AudioParams& params);
+18 -6
View File
@@ -27,16 +27,18 @@
OLIVE_NAMESPACE_ENTER
CacheTask::CacheTask(RenderBackend *backend, bool in_out_only) :
RenderTask(backend),
in_out_only_(in_out_only)
{
Init();
}
CacheTask::CacheTask(ViewerOutput* viewer, const VideoParams& vparams, const AudioParams &aparams, bool in_out_only) :
RenderTask(viewer, vparams, aparams),
in_out_only_(in_out_only)
{
SetTitle(tr("Caching \"%1\"").arg(viewer->media_name()));
backend()->EnablePreviewGeneration(job_time());
// Render fastest quality
backend()->SetRenderMode(RenderMode::kOffline);
Init();
}
bool CacheTask::Run()
@@ -83,4 +85,14 @@ void CacheTask::AudioDownloaded(const TimeRange &range, SampleBufferPtr samples)
}
}
void CacheTask::Init()
{
SetTitle(tr("Caching \"%1\"").arg(viewer()->media_name()));
backend()->EnablePreviewGeneration(job_time());
// Render fastest quality
backend()->SetRenderMode(RenderMode::kOffline);
}
OLIVE_NAMESPACE_EXIT
+3
View File
@@ -31,6 +31,7 @@ class CacheTask : public RenderTask
{
Q_OBJECT
public:
CacheTask(RenderBackend* backend, bool in_out_only);
CacheTask(ViewerOutput* viewer,
const VideoParams &vparams,
const AudioParams &aparams,
@@ -46,6 +47,8 @@ protected:
virtual void AudioDownloaded(const TimeRange& range, SampleBufferPtr samples) override;
private:
void Init();
bool in_out_only_;
QThreadPool download_threads_;
+30 -17
View File
@@ -24,18 +24,31 @@
OLIVE_NAMESPACE_ENTER
RenderTask::RenderTask(ViewerOutput* viewer, const VideoParams &vparams, const AudioParams &aparams) :
viewer_(viewer),
video_params_(vparams),
audio_params_(aparams)
RenderTask::RenderTask(RenderBackend *backend) :
backend_(backend)
{
job_time_ = QDateTime::currentMSecsSinceEpoch();
// FIXME: This makes a full copy of the node graph every time it starts, there must be a better
// way.
backend_.SetViewerNode(viewer_);
backend_.SetVideoParams(video_params_);
backend_.SetAudioParams(audio_params_);
backend_is_ours_ = false;
}
RenderTask::RenderTask(ViewerOutput* viewer, const VideoParams &vparams, const AudioParams &aparams)
{
job_time_ = QDateTime::currentMSecsSinceEpoch();
backend_ = new OpenGLBackend();
backend_->SetViewerNode(viewer);
backend_->SetVideoParams(vparams);
backend_->SetAudioParams(aparams);
backend_is_ours_ = true;
}
RenderTask::~RenderTask()
{
if (backend_is_ours_) {
backend_->deleteLater();
}
}
struct TimeHashFuturePair {
@@ -68,11 +81,11 @@ void RenderTask::Render(const TimeRangeList& video_range,
const QMatrix4x4& mat,
bool use_disk_cache)
{
backend_.SetVideoDownloadMatrix(mat);
backend_->SetVideoDownloadMatrix(mat);
double progress_counter = 0;
double total_length = 0;
double video_frame_sz = video_params_.time_base().toDouble();
double video_frame_sz = video_params().time_base().toDouble();
std::list<TimeRange> audio_queue;
std::list<RangeSampleFuturePair> audio_lookup_table;
@@ -93,11 +106,11 @@ void RenderTask::Render(const TimeRangeList& video_range,
if (!video_range.isEmpty()) {
QList<QByteArray> existing_hashes;
times = viewer_->video_frame_cache()->GetFrameListFromTimeRange(video_range);
times = viewer()->video_frame_cache()->GetFrameListFromTimeRange(video_range);
total_length += video_frame_sz * times.size();
QFuture<QVector<QByteArray> > hash_future = backend_.Hash(times);
QFuture<QVector<QByteArray> > hash_future = backend_->Hash(times);
hashes = hash_future.result();
for (int i=0;i<times.size();i++) {
@@ -141,7 +154,7 @@ void RenderTask::Render(const TimeRangeList& video_range,
// If not, check if it's in the filesystem
if (!hash_exists) {
hash_exists = QFileInfo::exists(viewer_->video_frame_cache()->CachePathName(p.hash));
hash_exists = QFileInfo::exists(viewer()->video_frame_cache()->CachePathName(p.hash));
// If so, add it to the list so we don't have to check the filesystem again later
if (hash_exists) {
@@ -159,7 +172,7 @@ void RenderTask::Render(const TimeRangeList& video_range,
// If no existing disk cache was found, queue it now
if (!hash_exists) {
render_lookup_table.push_back({p.hash, backend_.RenderFrame(p.time)});
render_lookup_table.push_back({p.hash, backend_->RenderFrame(p.time)});
running_hashes.push_back(p.hash);
}
}
@@ -169,7 +182,7 @@ void RenderTask::Render(const TimeRangeList& video_range,
}
if (!IsCancelled() && !audio_queue.empty()) {
audio_lookup_table.push_back({audio_queue.front(), backend_.RenderAudio(audio_queue.front())});
audio_lookup_table.push_back({audio_queue.front(), backend_->RenderAudio(audio_queue.front())});
audio_queue.pop_front();
}
@@ -233,7 +246,7 @@ void RenderTask::Render(const TimeRangeList& video_range,
}
// `Close` will block until all jobs are done making a safe deletion
backend_.Close();
backend_->Close();
}
void RenderTask::SetAnchorPoint(const rational &r)
+11 -12
View File
@@ -32,8 +32,11 @@ OLIVE_NAMESPACE_ENTER
class RenderTask : public Task
{
public:
RenderTask(RenderBackend* backend);
RenderTask(ViewerOutput* viewer, const VideoParams &vparams, const AudioParams &aparams);
virtual ~RenderTask() override;
protected:
void Render(const TimeRangeList &video_range,
const TimeRangeList &audio_range,
@@ -48,17 +51,17 @@ protected:
ViewerOutput* viewer() const
{
return viewer_;
return backend_->GetViewerNode();
}
VideoParams video_params() const
{
return video_params_;
return backend_->GetVideoParams();
}
AudioParams audio_params() const
{
return audio_params_;
return backend_->GetAudioParams();
}
void SetAnchorPoint(const rational& r);
@@ -68,21 +71,17 @@ protected:
return job_time_;
}
OpenGLBackend* backend()
RenderBackend* backend()
{
return &backend_;
return backend_;
}
private:
ViewerOutput* viewer_;
VideoParams video_params_;
AudioParams audio_params_;
rational anchor_point_;
OpenGLBackend backend_;
RenderBackend* backend_;
bool backend_is_ours_;
qint64 job_time_;