render one frame per thread for increased parallelism
If the nodes are now stateless, there's nothing stopping the renderer from rendering multiple frames at once. Earlier since the nodes held some of their input/output data (and that data could change per frame), it was not possible to render multiple frames at once without conflicts. Now that the node state is held in render threads, they can do whatever they want at any time.
This commit is contained in:
+2
-2
@@ -283,7 +283,7 @@ void NodeInput::set_maximum(const QVariant &max)
|
||||
has_maximum_ = true;
|
||||
}
|
||||
|
||||
void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_connections)
|
||||
void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_connections, bool lock_connections)
|
||||
{
|
||||
Q_ASSERT(source->id() == dest->id());
|
||||
|
||||
@@ -295,7 +295,7 @@ void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_conn
|
||||
|
||||
// Copy connections
|
||||
if (include_connections && source->get_connected_output() != nullptr) {
|
||||
ConnectEdge(source->get_connected_output(), dest);
|
||||
ConnectEdge(source->get_connected_output(), dest, lock_connections);
|
||||
}
|
||||
|
||||
// If these inputs are an array, copy the subparams too
|
||||
|
||||
+1
-1
@@ -121,7 +121,7 @@ public:
|
||||
/**
|
||||
* @brief Copy all values including keyframe information and connections from another NodeInput
|
||||
*/
|
||||
static void CopyValues(NodeInput* source, NodeInput* dest, bool include_connections = true);
|
||||
static void CopyValues(NodeInput* source, NodeInput* dest, bool include_connections = true, bool lock_connections = true);
|
||||
|
||||
signals:
|
||||
void ValueChanged(const rational& start, const rational& end);
|
||||
|
||||
+1
-1
@@ -182,7 +182,7 @@ void Node::CopyInputs(Node *source, Node *destination, bool include_connections)
|
||||
if (src->dependent()) {
|
||||
NodeInput* dst = static_cast<NodeInput*>(dst_param.at(i));
|
||||
|
||||
NodeInput::CopyValues(src, dst, include_connections);
|
||||
NodeInput::CopyValues(src, dst, include_connections, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-5
@@ -103,7 +103,7 @@ void NodeParam::DisconnectAll()
|
||||
}
|
||||
}
|
||||
|
||||
NodeEdgePtr NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input)
|
||||
NodeEdgePtr NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input, bool lock)
|
||||
{
|
||||
// If the input can only accept one input (the default) and has one already, disconnect it
|
||||
DisconnectForNewOutput(input);
|
||||
@@ -127,14 +127,18 @@ NodeEdgePtr NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input)
|
||||
// that's difficult to diagnose. This makes that issue very clear.
|
||||
Q_ASSERT(output->parentNode() != input->parentNode());
|
||||
|
||||
output->parentNode()->LockUserInput();
|
||||
input->parentNode()->LockUserInput();
|
||||
if (lock) {
|
||||
output->parentNode()->LockUserInput();
|
||||
input->parentNode()->LockUserInput();
|
||||
}
|
||||
|
||||
output->edges_.append(edge);
|
||||
input->edges_.append(edge);
|
||||
|
||||
output->parentNode()->UnlockUserInput();
|
||||
input->parentNode()->UnlockUserInput();
|
||||
if (lock) {
|
||||
output->parentNode()->UnlockUserInput();
|
||||
input->parentNode()->UnlockUserInput();
|
||||
}
|
||||
|
||||
// Emit a signal than an edge was added (only one signal needs emitting)
|
||||
emit input->EdgeAdded(edge);
|
||||
|
||||
+1
-1
@@ -292,7 +292,7 @@ public:
|
||||
*
|
||||
* This function emits EdgeAdded().
|
||||
*/
|
||||
static NodeEdgePtr ConnectEdge(NodeOutput *output, NodeInput *input);
|
||||
static NodeEdgePtr ConnectEdge(NodeOutput *output, NodeInput *input, bool lock = true);
|
||||
|
||||
/**
|
||||
* @brief Disconnect an edge
|
||||
|
||||
@@ -37,7 +37,7 @@ bool AudioBackend::InitInternal()
|
||||
// Initiate one thread per CPU core
|
||||
for (int i=0;i<threads().size();i++) {
|
||||
// Create one processor object for each thread
|
||||
AudioWorker* processor = new AudioWorker(decoder_cache());
|
||||
AudioWorker* processor = new AudioWorker();
|
||||
processor->SetParameters(params());
|
||||
processors_.append(processor);
|
||||
}
|
||||
@@ -67,8 +67,6 @@ void AudioBackend::ConnectWorkerToThis(RenderWorker *worker)
|
||||
|
||||
void AudioBackend::ThreadCompletedCache(NodeDependency dep, NodeValueTable data)
|
||||
{
|
||||
caching_ = false;
|
||||
|
||||
QByteArray cached_samples = data.Get(NodeParam::kSamples).toByteArray();
|
||||
|
||||
int offset = params().time_to_bytes(dep.in());
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#include "audioworker.h"
|
||||
|
||||
AudioWorker::AudioWorker(DecoderCache *decoder_cache, QObject *parent) :
|
||||
AudioRenderWorker(decoder_cache, parent)
|
||||
AudioWorker::AudioWorker(QObject *parent) :
|
||||
AudioRenderWorker(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void AudioWorker::FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable *table)
|
||||
{
|
||||
Q_UNUSED(stream)
|
||||
|
||||
table->Push(NodeParam::kSamples, frame->ToByteArray());
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
class AudioWorker : public AudioRenderWorker
|
||||
{
|
||||
public:
|
||||
AudioWorker(DecoderCache* decoder_cache, QObject* parent = nullptr);
|
||||
AudioWorker(QObject* parent = nullptr);
|
||||
|
||||
protected:
|
||||
virtual void FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable* table) override;
|
||||
|
||||
@@ -29,6 +29,8 @@ void AudioRenderBackend::SetParameters(const AudioRenderingParams ¶ms)
|
||||
|
||||
void AudioRenderBackend::InvalidateCache(const rational &start_range, const rational &end_range)
|
||||
{
|
||||
RenderBackend::InvalidateCache(start_range, end_range);
|
||||
|
||||
rational start_range_adj = qMax(rational(0), start_range);
|
||||
rational end_range_adj = qMin(SequenceLength(), end_range);
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
#include "audio/audiomanager.h"
|
||||
|
||||
AudioRenderWorker::AudioRenderWorker(DecoderCache *decoder_cache, QObject *parent) :
|
||||
RenderWorker(decoder_cache, parent)
|
||||
AudioRenderWorker::AudioRenderWorker(QObject *parent) :
|
||||
RenderWorker(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ class AudioRenderWorker : public RenderWorker
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AudioRenderWorker(DecoderCache* decoder_cache, QObject* parent = nullptr);
|
||||
AudioRenderWorker(QObject* parent = nullptr);
|
||||
|
||||
void SetParameters(const AudioRenderingParams& audio_params);
|
||||
|
||||
|
||||
@@ -5,6 +5,6 @@
|
||||
#include "project/item/footage/stream.h"
|
||||
#include "rendercache.h"
|
||||
|
||||
using DecoderCache = RenderCache<Stream*, DecoderPtr>;
|
||||
using DecoderCache = ThreadSafeRenderCache<Stream*, DecoderPtr>;
|
||||
|
||||
#endif // DECODERCACHE_H
|
||||
|
||||
@@ -27,7 +27,6 @@ set(OLIVE_SOURCES
|
||||
render/backend/opengl/openglshader.h
|
||||
render/backend/opengl/openglshader.cpp
|
||||
render/backend/opengl/openglshadercache.h
|
||||
render/backend/opengl/openglshadercache.cpp
|
||||
render/backend/opengl/opengltexture.h
|
||||
render/backend/opengl/opengltexture.cpp
|
||||
render/backend/opengl/openglworker.h
|
||||
|
||||
@@ -31,7 +31,7 @@ bool OpenGLBackend::InitInternal()
|
||||
// Initiate one thread per CPU core
|
||||
for (int i=0;i<threads().size();i++) {
|
||||
// Create one processor object for each thread
|
||||
OpenGLWorker* processor = new OpenGLWorker(share_ctx, &shader_cache_, decoder_cache(), color_cache(), frame_cache());
|
||||
OpenGLWorker* processor = new OpenGLWorker(share_ctx, &shader_cache_, frame_cache());
|
||||
processor->SetParameters(params());
|
||||
processors_.append(processor);
|
||||
}
|
||||
@@ -76,14 +76,14 @@ bool OpenGLBackend::CompileInternal()
|
||||
|
||||
foreach (Node* n, nodes) {
|
||||
// Check if we have a shader or not
|
||||
if (!shader_cache_.HasShader(n)) {
|
||||
if (!shader_cache_.Has(n->id())) {
|
||||
// Since we don't have a shader, compile one now
|
||||
QString node_code = n->Code();
|
||||
|
||||
// If the node has no code, it mustn't be GPU accelerated
|
||||
if (node_code.isEmpty()) {
|
||||
// We enter a null shader so we don't try to compile this again
|
||||
shader_cache_.AddShader(n, nullptr);
|
||||
shader_cache_.Add(n->id(), nullptr);
|
||||
} else {
|
||||
// Since we have shader code, compile it now
|
||||
OpenGLShaderPtr program;
|
||||
@@ -113,7 +113,7 @@ bool OpenGLBackend::CompileInternal()
|
||||
return false;
|
||||
}
|
||||
|
||||
shader_cache_.AddShader(n, program);
|
||||
shader_cache_.Add(n->id(), program);
|
||||
|
||||
//qDebug() << "Compiled" << connected_output->parent()->id() << "->" << connected_output->id();
|
||||
}
|
||||
@@ -166,26 +166,33 @@ void OpenGLBackend::ThreadCompletedFrame(NodeDependency path, QByteArray hash, N
|
||||
emit CachedFrameReady(path.in(), value);
|
||||
}
|
||||
|
||||
CompletedFrame();
|
||||
// Queue up a new frame for this worker
|
||||
CacheNext();
|
||||
}
|
||||
|
||||
void OpenGLBackend::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);
|
||||
foreach (const rational& t, times_with_this_hash) {
|
||||
emit CachedTimeReady(t);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLBackend::ThreadSkippedFrame(NodeDependency dep, QByteArray hash)
|
||||
{
|
||||
frame_cache()->SetHash(dep.in(), hash);
|
||||
|
||||
CompletedFrame();
|
||||
// Queue up a new frame for this worker
|
||||
CacheNext();
|
||||
}
|
||||
|
||||
void OpenGLBackend::ThreadHashAlreadyExists(NodeDependency dep, QByteArray hash)
|
||||
{
|
||||
ThreadCompletedDownload(dep, hash);
|
||||
|
||||
CompletedFrame();
|
||||
// Queue up a new frame for this worker
|
||||
CacheNext();
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
#include "openglshadercache.h"
|
||||
|
||||
#include "node/node.h"
|
||||
|
||||
void OpenGLShaderCache::Clear()
|
||||
{
|
||||
compiled_nodes_.clear();
|
||||
}
|
||||
|
||||
void OpenGLShaderCache::AddShader(Node *output, OpenGLShaderPtr shader)
|
||||
{
|
||||
compiled_nodes_.insert(GenerateShaderID(output), shader);
|
||||
}
|
||||
|
||||
OpenGLShaderPtr OpenGLShaderCache::GetShader(Node *output)
|
||||
{
|
||||
return compiled_nodes_.value(GenerateShaderID(output));
|
||||
}
|
||||
|
||||
QString OpenGLShaderCache::GenerateShaderID(Node *output)
|
||||
{
|
||||
return output->id();
|
||||
}
|
||||
|
||||
bool OpenGLShaderCache::HasShader(Node *output)
|
||||
{
|
||||
return compiled_nodes_.contains(GenerateShaderID(output));
|
||||
}
|
||||
@@ -1,32 +1,11 @@
|
||||
#ifndef OPENGLSHADERCACHE_H
|
||||
#define OPENGLSHADERCACHE_H
|
||||
|
||||
#include <QMutex>
|
||||
#include <QString>
|
||||
|
||||
#include "node/output.h"
|
||||
#include "openglshader.h"
|
||||
#include "render/backend/rendercache.h"
|
||||
|
||||
/**
|
||||
* @brief Thread-safe cache of OpenGL shaders
|
||||
*/
|
||||
class OpenGLShaderCache
|
||||
{
|
||||
public:
|
||||
OpenGLShaderCache() = default;
|
||||
|
||||
void Clear();
|
||||
|
||||
void AddShader(Node* output, OpenGLShaderPtr shader);
|
||||
|
||||
OpenGLShaderPtr GetShader(Node* output);
|
||||
|
||||
bool HasShader(Node* output);
|
||||
|
||||
private:
|
||||
QString GenerateShaderID(Node *output);
|
||||
|
||||
QMap<QString, OpenGLShaderPtr> compiled_nodes_;
|
||||
|
||||
};
|
||||
using OpenGLShaderCache = RenderCache<QString, OpenGLShaderPtr>;
|
||||
|
||||
#endif // OPENGLSHADERCACHE_H
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
#include "render/colormanager.h"
|
||||
#include "render/pixelservice.h"
|
||||
|
||||
OpenGLWorker::OpenGLWorker(QOpenGLContext *share_ctx, OpenGLShaderCache *shader_cache, DecoderCache *decoder_cache, ColorProcessorCache *color_cache, VideoRenderFrameCache *frame_cache, QObject *parent) :
|
||||
VideoRenderWorker(decoder_cache, color_cache, frame_cache, parent),
|
||||
OpenGLWorker::OpenGLWorker(QOpenGLContext *share_ctx, OpenGLShaderCache *shader_cache, VideoRenderFrameCache *frame_cache, QObject *parent) :
|
||||
VideoRenderWorker(frame_cache, parent),
|
||||
share_ctx_(share_ctx),
|
||||
ctx_(nullptr),
|
||||
functions_(nullptr),
|
||||
@@ -143,7 +143,7 @@ void OpenGLWorker::ParametersChangedEvent()
|
||||
|
||||
void OpenGLWorker::RunNodeAccelerated(Node *node, const NodeValueDatabase *input_params, NodeValueTable *output_params)
|
||||
{
|
||||
OpenGLShaderPtr shader = shader_cache_->GetShader(node);
|
||||
OpenGLShaderPtr shader = shader_cache_->Get(node->id());
|
||||
|
||||
if (!shader) {
|
||||
return;
|
||||
|
||||
@@ -13,8 +13,6 @@ class OpenGLWorker : public VideoRenderWorker {
|
||||
public:
|
||||
OpenGLWorker(QOpenGLContext* share_ctx,
|
||||
OpenGLShaderCache* shader_cache,
|
||||
DecoderCache* decoder_cache,
|
||||
ColorProcessorCache *color_cache,
|
||||
VideoRenderFrameCache* frame_cache,
|
||||
QObject* parent = nullptr);
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
RenderBackend::RenderBackend(QObject *parent) :
|
||||
QObject(parent),
|
||||
compiled_(false),
|
||||
caching_(false),
|
||||
started_(false),
|
||||
viewer_node_(nullptr),
|
||||
copied_viewer_node_(nullptr),
|
||||
value_update_queued_(false),
|
||||
recompile_queued_(false)
|
||||
recompile_queued_(false),
|
||||
input_update_queued_(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -53,17 +53,22 @@ void RenderBackend::Close()
|
||||
|
||||
CloseInternal();
|
||||
|
||||
decoder_cache_.Clear();
|
||||
for (int i=0;i<processors_.size();i++) {
|
||||
// Invoke close and quit signals on processor and thred
|
||||
QMetaObject::invokeMethod(processors_.at(i),
|
||||
"Close",
|
||||
Qt::QueuedConnection);
|
||||
|
||||
foreach (QThread* thread, threads_) {
|
||||
thread->quit();
|
||||
thread->wait(); // FIXME: Maximum time in case a thread is stuck?
|
||||
threads_.at(i)->quit();
|
||||
}
|
||||
|
||||
for (int i=0;i<processors_.size();i++) {
|
||||
threads_.at(i)->wait(); // FIXME: Maximum time in case a thread is stuck?
|
||||
delete threads_.at(i);
|
||||
delete processors_.at(i);
|
||||
}
|
||||
|
||||
threads_.clear();
|
||||
|
||||
foreach (RenderWorker* processor, processors_) {
|
||||
delete processor;
|
||||
}
|
||||
processors_.clear();
|
||||
}
|
||||
|
||||
@@ -100,12 +105,17 @@ bool RenderBackend::IsInitiated()
|
||||
return started_;
|
||||
}
|
||||
|
||||
void RenderBackend::InvalidateCache(const rational &start_range, const rational &end_range)
|
||||
{
|
||||
Q_UNUSED(start_range)
|
||||
Q_UNUSED(end_range)
|
||||
|
||||
input_update_queued_ = true;
|
||||
}
|
||||
|
||||
bool RenderBackend::Compile()
|
||||
{
|
||||
if (recompile_queued_) {
|
||||
Decompile();
|
||||
recompile_queued_ = false;
|
||||
} else if (compiled_) {
|
||||
if (compiled_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -199,39 +209,48 @@ void RenderBackend::DisconnectViewer(ViewerOutput *node)
|
||||
|
||||
void RenderBackend::CacheNext()
|
||||
{
|
||||
if (!Init() || cache_queue_.isEmpty() || !ViewerIsConnected() || caching_) {
|
||||
if (!Init()
|
||||
|| cache_queue_.isEmpty()
|
||||
|| !ViewerIsConnected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateNodeInputs();
|
||||
|
||||
TimeRange cache_frame = cache_queue_.takeFirst();
|
||||
|
||||
//qDebug() << "Caching" << cache_frame.in();
|
||||
|
||||
caching_ = GenerateData(cache_frame);
|
||||
}
|
||||
|
||||
bool RenderBackend::GenerateData(const TimeRange &range)
|
||||
{
|
||||
if (!Compile()) {
|
||||
qDebug() << "Graph remains uncompiled, nothing to be done";
|
||||
return false;
|
||||
if ((input_update_queued_ || recompile_queued_) && !AllProcessorsAreAvailable()) {
|
||||
qDebug() << "Blocking render while we wait for all workers to finish...";
|
||||
return;
|
||||
}
|
||||
|
||||
NodeDependency dep = NodeDependency(GetDependentInput()->get_connected_node(), range.in(), range.out());
|
||||
if (input_update_queued_) {
|
||||
UpdateNodeInputs();
|
||||
input_update_queued_ = false;
|
||||
}
|
||||
|
||||
if (recompile_queued_) {
|
||||
Decompile();
|
||||
recompile_queued_ = false;
|
||||
}
|
||||
|
||||
if (!compiled_ && !Compile()) {
|
||||
qDebug() << "Graph remains uncompiled, nothing to be done";
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (RenderWorker* worker, processors_) {
|
||||
if (worker->IsAvailable() || worker == processors_.last()) {
|
||||
if (cache_queue_.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (worker->IsAvailable()) {
|
||||
TimeRange cache_frame = cache_queue_.takeFirst();
|
||||
|
||||
NodeDependency dep = NodeDependency(GetDependentInput()->get_connected_node(), cache_frame.in(), cache_frame.out());
|
||||
|
||||
QMetaObject::invokeMethod(worker,
|
||||
"Render",
|
||||
Qt::QueuedConnection,
|
||||
Q_ARG(NodeDependency, dep));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ViewerOutput *RenderBackend::viewer_node() const
|
||||
@@ -244,11 +263,6 @@ bool RenderBackend::ViewerIsConnected() const
|
||||
return viewer_node_ != nullptr;
|
||||
}
|
||||
|
||||
DecoderCache *RenderBackend::decoder_cache()
|
||||
{
|
||||
return &decoder_cache_;
|
||||
}
|
||||
|
||||
const QString &RenderBackend::cache_id() const
|
||||
{
|
||||
return cache_id_;
|
||||
@@ -274,6 +288,17 @@ void RenderBackend::UpdateNodeInputs()
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderBackend::AllProcessorsAreAvailable() const
|
||||
{
|
||||
foreach (RenderWorker* worker, processors_) {
|
||||
if (!worker->IsAvailable()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const QVector<QThread *> &RenderBackend::threads()
|
||||
{
|
||||
return threads_;
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
bool IsInitiated();
|
||||
|
||||
public slots:
|
||||
virtual void InvalidateCache(const rational &start_range, const rational &end_range) = 0;
|
||||
virtual void InvalidateCache(const rational &start_range, const rational &end_range);
|
||||
|
||||
bool Compile();
|
||||
|
||||
@@ -70,8 +70,6 @@ protected:
|
||||
*/
|
||||
void CacheNext();
|
||||
|
||||
bool GenerateData(const TimeRange& range);
|
||||
|
||||
void InitWorkers();
|
||||
|
||||
virtual NodeInput* GetDependentInput() = 0;
|
||||
@@ -82,8 +80,6 @@ protected:
|
||||
|
||||
bool ViewerIsConnected() const;
|
||||
|
||||
DecoderCache* decoder_cache();
|
||||
|
||||
const QString& cache_id() const;
|
||||
|
||||
void QueueValueUpdate(const TimeRange& range);
|
||||
@@ -96,9 +92,9 @@ protected:
|
||||
|
||||
bool compiled_;
|
||||
|
||||
bool caching_;
|
||||
|
||||
private:
|
||||
bool AllProcessorsAreAvailable() const;
|
||||
|
||||
/**
|
||||
* @brief Internal list of RenderProcessThreads
|
||||
*/
|
||||
@@ -124,8 +120,6 @@ private:
|
||||
*/
|
||||
QString error_;
|
||||
|
||||
DecoderCache decoder_cache_;
|
||||
|
||||
QString cache_name_;
|
||||
qint64 cache_time_;
|
||||
QString cache_id_;
|
||||
@@ -137,6 +131,7 @@ private:
|
||||
TimeRange value_update_range_;
|
||||
|
||||
bool recompile_queued_;
|
||||
bool input_update_queued_;
|
||||
|
||||
private slots:
|
||||
void ThreadRequestedSibling(NodeDependency dep);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define RENDERCACHE_H
|
||||
|
||||
#include <QMap>
|
||||
#include <QMutex>
|
||||
|
||||
template<class K, class V>
|
||||
class RenderCache
|
||||
@@ -11,12 +12,53 @@ public:
|
||||
|
||||
void Clear(){values_.clear();}
|
||||
|
||||
void Add(K stream, V shader){values_.insert(stream, shader);}
|
||||
void Add(K key, V val){values_.insert(key, val);}
|
||||
|
||||
V Get(K stream){return values_.value(stream);}
|
||||
V Get(K key) const {return values_.value(key);}
|
||||
|
||||
bool Has(K key) const {return values_.contains(key);}
|
||||
|
||||
private:
|
||||
QMap<K, V> values_;
|
||||
};
|
||||
|
||||
template<class K, class V>
|
||||
class ThreadSafeRenderCache
|
||||
{
|
||||
public:
|
||||
ThreadSafeRenderCache() = default;
|
||||
|
||||
void Clear() {
|
||||
lock_.lock();
|
||||
values_.clear();
|
||||
lock_.unlock();
|
||||
}
|
||||
|
||||
void Add(K key, V val) {
|
||||
lock_.lock();
|
||||
values_.insert(key, val);
|
||||
lock_.unlock();
|
||||
}
|
||||
|
||||
V Get(K key) {
|
||||
lock_.lock();
|
||||
V val = values_.value(key);
|
||||
lock_.unlock();
|
||||
return val;
|
||||
}
|
||||
|
||||
bool Has(K key) {
|
||||
lock_.lock();
|
||||
bool has = values_.contains(key);
|
||||
lock_.unlock();
|
||||
return has;
|
||||
}
|
||||
|
||||
private:
|
||||
QMap<K, V> values_;
|
||||
|
||||
QMutex lock_;
|
||||
|
||||
};
|
||||
|
||||
#endif // RENDERCACHE_H
|
||||
|
||||
@@ -4,17 +4,16 @@
|
||||
|
||||
#include "node/block/block.h"
|
||||
|
||||
RenderWorker::RenderWorker(DecoderCache *decoder_cache, QObject *parent) :
|
||||
RenderWorker::RenderWorker(QObject *parent) :
|
||||
QObject(parent),
|
||||
working_(0),
|
||||
started_(false),
|
||||
decoder_cache_(decoder_cache)
|
||||
started_(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool RenderWorker::IsAvailable()
|
||||
{
|
||||
return (working_ == 0);
|
||||
return !working_;
|
||||
}
|
||||
|
||||
bool RenderWorker::Init()
|
||||
@@ -34,6 +33,8 @@ void RenderWorker::Close()
|
||||
{
|
||||
CloseInternal();
|
||||
|
||||
decoder_cache_.Clear();
|
||||
|
||||
started_ = false;
|
||||
}
|
||||
|
||||
@@ -70,11 +71,6 @@ NodeValueTable RenderWorker::RenderAsSibling(NodeDependency dep)
|
||||
return value;
|
||||
}
|
||||
|
||||
DecoderCache *RenderWorker::decoder_cache()
|
||||
{
|
||||
return decoder_cache_;
|
||||
}
|
||||
|
||||
NodeValueTable RenderWorker::RenderInternal(const NodeDependency &path)
|
||||
{
|
||||
return RenderAsSibling(path);
|
||||
@@ -101,13 +97,13 @@ StreamPtr RenderWorker::ResolveStreamFromInput(NodeInput *input)
|
||||
DecoderPtr RenderWorker::ResolveDecoderFromInput(StreamPtr stream)
|
||||
{
|
||||
// Access a map of Node inputs and decoder instances and retrieve a frame!
|
||||
DecoderPtr decoder = decoder_cache()->Get(stream.get());
|
||||
DecoderPtr decoder = decoder_cache_.Get(stream.get());
|
||||
|
||||
if (decoder == nullptr && stream != nullptr) {
|
||||
// Create a new Decoder here
|
||||
DecoderPtr decoder = Decoder::CreateFromID(stream->footage()->decoder());
|
||||
decoder = Decoder::CreateFromID(stream->footage()->decoder());
|
||||
decoder->set_stream(stream);
|
||||
decoder_cache()->Add(stream.get(), decoder);
|
||||
decoder_cache_.Add(stream.get(), decoder);
|
||||
}
|
||||
|
||||
return decoder;
|
||||
|
||||
@@ -12,7 +12,7 @@ class RenderWorker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
RenderWorker(DecoderCache* decoder_cache, QObject* parent = nullptr);
|
||||
RenderWorker(QObject* parent = nullptr);
|
||||
|
||||
DISABLE_COPY_MOVE(RenderWorker)
|
||||
|
||||
@@ -56,14 +56,12 @@ protected:
|
||||
|
||||
virtual NodeValueTable RenderBlock(TrackOutput *track, const TimeRange& range) = 0;
|
||||
|
||||
DecoderCache* decoder_cache();
|
||||
|
||||
QAtomicInt working_;
|
||||
|
||||
private:
|
||||
bool started_;
|
||||
|
||||
DecoderCache* decoder_cache_;
|
||||
DecoderCache decoder_cache_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -43,6 +43,8 @@ void VideoRenderBackend::InvalidateCache(const rational &start_range, const rati
|
||||
return;
|
||||
}
|
||||
|
||||
RenderBackend::InvalidateCache(start_range, end_range);
|
||||
|
||||
// Adjust range to min/max values
|
||||
rational start_range_adj = qMax(rational(0), start_range);
|
||||
rational end_range_adj = qMin(SequenceLength(), end_range);
|
||||
@@ -185,11 +187,6 @@ VideoRenderFrameCache *VideoRenderBackend::frame_cache()
|
||||
return &frame_cache_;
|
||||
}
|
||||
|
||||
ColorProcessorCache *VideoRenderBackend::color_cache()
|
||||
{
|
||||
return &color_cache_;
|
||||
}
|
||||
|
||||
const char *VideoRenderBackend::GetCachedFrame(const rational &time)
|
||||
{
|
||||
last_time_requested_ = time;
|
||||
@@ -237,9 +234,3 @@ NodeInput *VideoRenderBackend::GetDependentInput()
|
||||
{
|
||||
return viewer_node()->texture_input();
|
||||
}
|
||||
|
||||
void VideoRenderBackend::CompletedFrame()
|
||||
{
|
||||
caching_ = false;
|
||||
CacheNext();
|
||||
}
|
||||
|
||||
@@ -82,8 +82,6 @@ protected:
|
||||
|
||||
VideoRenderFrameCache* frame_cache();
|
||||
|
||||
ColorProcessorCache* color_cache();
|
||||
|
||||
const VideoRenderingParams& params() const;
|
||||
|
||||
/**
|
||||
@@ -95,8 +93,6 @@ protected:
|
||||
|
||||
virtual void ConnectWorkerToThis(RenderWorker* processor) override;
|
||||
|
||||
void CompletedFrame();
|
||||
|
||||
signals:
|
||||
void CachedFrameReady(const rational& time, QVariant value);
|
||||
void CachedTimeReady(const rational& time);
|
||||
@@ -108,8 +104,6 @@ private:
|
||||
|
||||
VideoRenderFrameCache frame_cache_;
|
||||
|
||||
ColorProcessorCache color_cache_;
|
||||
|
||||
rational last_time_requested_;
|
||||
|
||||
private slots:
|
||||
|
||||
@@ -46,7 +46,7 @@ void VideoRenderFrameCache::SetCacheID(const QString &id)
|
||||
cache_id_ = id;
|
||||
}
|
||||
|
||||
QByteArray VideoRenderFrameCache::TimeToHash(const rational &time)
|
||||
QByteArray VideoRenderFrameCache::TimeToHash(const rational &time) const
|
||||
{
|
||||
return time_hash_map_.value(time);
|
||||
}
|
||||
@@ -80,6 +80,21 @@ void VideoRenderFrameCache::Truncate(const rational &time)
|
||||
}
|
||||
}
|
||||
|
||||
QList<rational> VideoRenderFrameCache::TimesWithHash(const QByteArray &hash)
|
||||
{
|
||||
QList<rational> list;
|
||||
|
||||
QMap<rational, QByteArray>::const_iterator iterator;
|
||||
|
||||
for (iterator=time_hash_map_.begin();iterator!=time_hash_map_.end();iterator++) {
|
||||
if (iterator.value() == hash) {
|
||||
list.append(iterator.key());
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void VideoRenderFrameCache::RemoveHashFromCurrentlyCaching(const QByteArray &hash)
|
||||
{
|
||||
currently_caching_lock_.lock();
|
||||
@@ -87,7 +102,7 @@ void VideoRenderFrameCache::RemoveHashFromCurrentlyCaching(const QByteArray &has
|
||||
currently_caching_lock_.unlock();
|
||||
}
|
||||
|
||||
QString VideoRenderFrameCache::CachePathName(const QByteArray &hash)
|
||||
QString VideoRenderFrameCache::CachePathName(const QByteArray &hash) const
|
||||
{
|
||||
QDir this_cache_dir = QDir(GetMediaCacheLocation()).filePath(cache_id_);
|
||||
this_cache_dir.mkpath(".");
|
||||
|
||||
@@ -28,17 +28,19 @@ public:
|
||||
/**
|
||||
* @brief Return the path of the cached image at this time
|
||||
*/
|
||||
QString CachePathName(const QByteArray &hash);
|
||||
QString CachePathName(const QByteArray &hash) const;
|
||||
|
||||
void SetCacheID(const QString& id);
|
||||
|
||||
QByteArray TimeToHash(const rational& time);
|
||||
QByteArray TimeToHash(const rational& time) const;
|
||||
|
||||
void SetHash(const rational& time, const QByteArray& hash);
|
||||
void RemoveHash(const rational& time, const QByteArray &hash);
|
||||
|
||||
void Truncate(const rational& time);
|
||||
|
||||
QList<rational> TimesWithHash(const QByteArray& hash);
|
||||
|
||||
private:
|
||||
void RemoveHashFromCurrentlyCaching(const QByteArray& hash);
|
||||
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
#include "node/node.h"
|
||||
#include "render/pixelservice.h"
|
||||
|
||||
VideoRenderWorker::VideoRenderWorker(DecoderCache *decoder_cache, ColorProcessorCache *color_cache, VideoRenderFrameCache *frame_cache, QObject *parent) :
|
||||
RenderWorker(decoder_cache, parent),
|
||||
frame_cache_(frame_cache),
|
||||
color_cache_(color_cache)
|
||||
VideoRenderWorker::VideoRenderWorker(VideoRenderFrameCache *frame_cache, QObject *parent) :
|
||||
RenderWorker(parent),
|
||||
frame_cache_(frame_cache)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -19,6 +18,8 @@ const VideoRenderingParams &VideoRenderWorker::video_params()
|
||||
|
||||
NodeValueTable VideoRenderWorker::RenderInternal(const NodeDependency& path)
|
||||
{
|
||||
qDebug() << "Rendering" << path.in().toDouble() << "on" << this;
|
||||
|
||||
// Get hash of node graph
|
||||
// We use SHA-1 for speed (benchmarks show it's the fastest hash available to us)
|
||||
QCryptographicHash hasher(QCryptographicHash::Sha1);
|
||||
@@ -182,7 +183,7 @@ NodeValueTable VideoRenderWorker::RenderBlock(TrackOutput *track, const TimeRang
|
||||
return table;
|
||||
}
|
||||
|
||||
ColorProcessorCache *VideoRenderWorker::color_cache() const
|
||||
ColorProcessorCache *VideoRenderWorker::color_cache()
|
||||
{
|
||||
return color_cache_;
|
||||
return &color_cache_;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
class VideoRenderWorker : public RenderWorker {
|
||||
Q_OBJECT
|
||||
public:
|
||||
VideoRenderWorker(DecoderCache* decoder_cache, ColorProcessorCache* color_cache, VideoRenderFrameCache* frame_cache, QObject* parent = nullptr);
|
||||
VideoRenderWorker(VideoRenderFrameCache* frame_cache, QObject* parent = nullptr);
|
||||
|
||||
void SetParameters(const VideoRenderingParams& video_params);
|
||||
|
||||
@@ -45,7 +45,7 @@ protected:
|
||||
|
||||
virtual NodeValueTable RenderBlock(TrackOutput *track, const TimeRange& range) override;
|
||||
|
||||
ColorProcessorCache* color_cache() const;
|
||||
ColorProcessorCache* color_cache();
|
||||
|
||||
private:
|
||||
void ProcessNode();
|
||||
@@ -56,7 +56,7 @@ private:
|
||||
|
||||
VideoRenderFrameCache* frame_cache_;
|
||||
|
||||
ColorProcessorCache* color_cache_;
|
||||
ColorProcessorCache color_cache_;
|
||||
|
||||
QByteArray download_buffer_;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user