Merge branch 'master' into track-arrays
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
|
||||
|
||||
@@ -35,7 +35,7 @@ Footage::~Footage()
|
||||
ClearStreams();
|
||||
}
|
||||
|
||||
const Footage::Status& Footage::status()
|
||||
const Footage::Status& Footage::status() const
|
||||
{
|
||||
return status_;
|
||||
}
|
||||
@@ -56,7 +56,7 @@ void Footage::Clear()
|
||||
set_status(kUnprobed);
|
||||
}
|
||||
|
||||
const QString &Footage::filename()
|
||||
const QString &Footage::filename() const
|
||||
{
|
||||
return filename_;
|
||||
}
|
||||
@@ -66,7 +66,7 @@ void Footage::set_filename(const QString &s)
|
||||
filename_ = s;
|
||||
}
|
||||
|
||||
const QDateTime &Footage::timestamp()
|
||||
const QDateTime &Footage::timestamp() const
|
||||
{
|
||||
return timestamp_;
|
||||
}
|
||||
@@ -85,17 +85,17 @@ void Footage::add_stream(StreamPtr s)
|
||||
streams_.last()->set_footage(this);
|
||||
}
|
||||
|
||||
StreamPtr Footage::stream(int index)
|
||||
StreamPtr Footage::stream(int index) const
|
||||
{
|
||||
return streams_.at(index);
|
||||
}
|
||||
|
||||
const QList<StreamPtr> &Footage::streams()
|
||||
const QList<StreamPtr> &Footage::streams() const
|
||||
{
|
||||
return streams_;
|
||||
}
|
||||
|
||||
int Footage::stream_count()
|
||||
int Footage::stream_count() const
|
||||
{
|
||||
return streams_.size();
|
||||
}
|
||||
@@ -105,7 +105,7 @@ Item::Type Footage::type() const
|
||||
return kFootage;
|
||||
}
|
||||
|
||||
const QString &Footage::decoder()
|
||||
const QString &Footage::decoder() const
|
||||
{
|
||||
return decoder_;
|
||||
}
|
||||
@@ -139,7 +139,7 @@ QIcon Footage::icon()
|
||||
return olive::icon::Image;
|
||||
|
||||
}
|
||||
/* fall through */
|
||||
/* fall-through */
|
||||
case kInvalid:
|
||||
return olive::icon::Error;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <QList>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "common/constructors.h"
|
||||
#include "common/rational.h"
|
||||
#include "project/item/item.h"
|
||||
#include "project/item/footage/audiostream.h"
|
||||
@@ -59,25 +60,7 @@ public:
|
||||
*/
|
||||
virtual ~Footage() override;
|
||||
|
||||
/**
|
||||
* @brief Deleted copy constructor
|
||||
*/
|
||||
Footage(const Footage& other) = delete;
|
||||
|
||||
/**
|
||||
* @brief Deleted move constructor
|
||||
*/
|
||||
Footage(Footage&& other) = delete;
|
||||
|
||||
/**
|
||||
* @brief Deleted copy assignment
|
||||
*/
|
||||
Footage& operator=(const Footage& other) = delete;
|
||||
|
||||
/**
|
||||
* @brief Deleted move assignment
|
||||
*/
|
||||
Footage& operator=(Footage&& other) = delete;
|
||||
DISABLE_COPY_MOVE(Footage)
|
||||
|
||||
/**
|
||||
* @brief Check the ready state of this Footage object
|
||||
@@ -86,7 +69,7 @@ public:
|
||||
*
|
||||
* If the Footage has been successfully probed, this will return TRUE.
|
||||
*/
|
||||
const Status& status();
|
||||
const Status& status() const;
|
||||
|
||||
/**
|
||||
* @brief Set ready state
|
||||
@@ -114,7 +97,7 @@ public:
|
||||
/**
|
||||
* @brief Return the current filename of this Footage object
|
||||
*/
|
||||
const QString& filename();
|
||||
const QString& filename() const;
|
||||
|
||||
/**
|
||||
* @brief Set the filename
|
||||
@@ -134,7 +117,7 @@ public:
|
||||
* The file's last modified timestamp is stored for potential organization in the ProjectExplorer. It can be
|
||||
* retrieved here.
|
||||
*/
|
||||
const QDateTime& timestamp();
|
||||
const QDateTime& timestamp() const;
|
||||
|
||||
/**
|
||||
* @brief Set the last modified time/date
|
||||
@@ -172,17 +155,17 @@ public:
|
||||
*
|
||||
* The stream at the index provided
|
||||
*/
|
||||
StreamPtr stream(int index);
|
||||
StreamPtr stream(int index) const;
|
||||
|
||||
/**
|
||||
* @brief Returns a list of the streams in this Footage
|
||||
*/
|
||||
const QList<StreamPtr>& streams();
|
||||
const QList<StreamPtr>& streams() const;
|
||||
|
||||
/**
|
||||
* @brief Retrieve total number of streams in this Footage file
|
||||
*/
|
||||
int stream_count();
|
||||
int stream_count() const;
|
||||
|
||||
/**
|
||||
* @brief Item::Type() override
|
||||
@@ -198,7 +181,7 @@ public:
|
||||
*
|
||||
* A decoder ID
|
||||
*/
|
||||
const QString& decoder();
|
||||
const QString& decoder() const;
|
||||
|
||||
/**
|
||||
* @brief Used by decoders when they Probe to attach itself to this Footage
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "stream.h"
|
||||
|
||||
#include "footage.h"
|
||||
#include "ui/icons/icons.h"
|
||||
|
||||
Stream::Stream() :
|
||||
@@ -114,3 +115,14 @@ QIcon Stream::IconFromType(const Stream::Type &type)
|
||||
|
||||
return QIcon();
|
||||
}
|
||||
|
||||
StreamID Stream::ToID() const
|
||||
{
|
||||
return StreamID(footage_->filename(), index_);
|
||||
}
|
||||
|
||||
StreamID::StreamID(const QString &filename, const int &stream_index) :
|
||||
filename_(filename),
|
||||
stream_index_(stream_index)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -29,6 +29,17 @@
|
||||
|
||||
class Footage;
|
||||
|
||||
class StreamID {
|
||||
public:
|
||||
StreamID(const QString& filename, const int& stream_index);
|
||||
|
||||
private:
|
||||
QString filename_;
|
||||
|
||||
int stream_index_;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A base class for keeping metadata about a media stream.
|
||||
*
|
||||
@@ -84,6 +95,8 @@ public:
|
||||
|
||||
static QIcon IconFromType(const Type& type);
|
||||
|
||||
StreamID ToID() const;
|
||||
|
||||
private:
|
||||
Footage* footage_;
|
||||
|
||||
|
||||
@@ -21,9 +21,6 @@ add_subdirectory(vulkan)
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
|
||||
render/backend/decodercache.h
|
||||
render/backend/decodercache.cpp
|
||||
|
||||
render/backend/renderbackend.h
|
||||
render/backend/renderbackend.cpp
|
||||
render/backend/renderworker.h
|
||||
@@ -40,6 +37,10 @@ set(OLIVE_SOURCES
|
||||
render/backend/videorenderframecache.cpp
|
||||
render/backend/videorenderworker.h
|
||||
render/backend/videorenderworker.cpp
|
||||
|
||||
render/backend/rendercache.h
|
||||
render/backend/colorprocessorcache.h
|
||||
render/backend/decodercache.h
|
||||
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
@@ -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,7 +67,7 @@ void AudioBackend::ConnectWorkerToThis(RenderWorker *worker)
|
||||
|
||||
void AudioBackend::ThreadCompletedCache(NodeDependency dep, NodeValueTable data)
|
||||
{
|
||||
caching_ = false;
|
||||
SetWorkerBusyState(static_cast<RenderWorker*>(sender()), false);
|
||||
|
||||
QByteArray cached_samples = data.Get(NodeParam::kSamples).toByteArray();
|
||||
|
||||
|
||||
@@ -1,11 +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(FramePtr frame, NodeValueTable *table)
|
||||
void AudioWorker::FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable *table)
|
||||
{
|
||||
Q_UNUSED(stream)
|
||||
|
||||
table->Push(NodeParam::kSamples, frame->ToByteArray());
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
class AudioWorker : public AudioRenderWorker
|
||||
{
|
||||
public:
|
||||
AudioWorker(DecoderCache* decoder_cache, QObject* parent = nullptr);
|
||||
AudioWorker(QObject* parent = nullptr);
|
||||
|
||||
protected:
|
||||
virtual void FrameToValue(FramePtr frame, NodeValueTable* table) override;
|
||||
virtual void FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable* table) override;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef COLORPROCESSORCACHE_H
|
||||
#define COLORPROCESSORCACHE_H
|
||||
|
||||
#include "project/item/footage/stream.h"
|
||||
#include "render/colorprocessor.h"
|
||||
#include "rendercache.h"
|
||||
|
||||
using ColorProcessorCache = RenderCache<Stream*, ColorProcessorPtr>;
|
||||
|
||||
#endif // COLORPROCESSORCACHE_H
|
||||
@@ -1,21 +0,0 @@
|
||||
#include "decodercache.h"
|
||||
|
||||
DecoderCache::DecoderCache()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void DecoderCache::Clear()
|
||||
{
|
||||
decoders_.clear();
|
||||
}
|
||||
|
||||
void DecoderCache::AddDecoder(Stream *stream, DecoderPtr shader)
|
||||
{
|
||||
decoders_.insert(stream, shader);
|
||||
}
|
||||
|
||||
DecoderPtr DecoderCache::GetDecoder(Stream *stream)
|
||||
{
|
||||
return decoders_.value(stream);
|
||||
}
|
||||
@@ -3,24 +3,8 @@
|
||||
|
||||
#include "decoder/decoder.h"
|
||||
#include "project/item/footage/stream.h"
|
||||
#include "rendercache.h"
|
||||
|
||||
/**
|
||||
* @brief Thread-safe cache of decoders
|
||||
*/
|
||||
class DecoderCache
|
||||
{
|
||||
public:
|
||||
DecoderCache();
|
||||
|
||||
void Clear();
|
||||
|
||||
void AddDecoder(Stream* stream, DecoderPtr shader);
|
||||
|
||||
DecoderPtr GetDecoder(Stream* stream);
|
||||
|
||||
private:
|
||||
QMap<Stream*, DecoderPtr> decoders_;
|
||||
|
||||
};
|
||||
using DecoderCache = ThreadSafeRenderCache<Stream*, DecoderPtr>;
|
||||
|
||||
#endif // DECODERCACHE_H
|
||||
|
||||
@@ -20,12 +20,13 @@ set(OLIVE_SOURCES
|
||||
render/backend/opengl/functions.cpp
|
||||
render/backend/opengl/openglbackend.h
|
||||
render/backend/opengl/openglbackend.cpp
|
||||
render/backend/opengl/openglcolorprocessor.h
|
||||
render/backend/opengl/openglcolorprocessor.cpp
|
||||
render/backend/opengl/openglframebuffer.h
|
||||
render/backend/opengl/openglframebuffer.cpp
|
||||
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
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
#include "functions.h"
|
||||
|
||||
OpenGLBackend::OpenGLBackend(QObject *parent) :
|
||||
VideoRenderBackend(parent)
|
||||
VideoRenderBackend(parent),
|
||||
last_download_thread_(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -31,7 +32,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(), frame_cache());
|
||||
OpenGLWorker* processor = new OpenGLWorker(share_ctx, &shader_cache_, frame_cache());
|
||||
processor->SetParameters(params());
|
||||
processors_.append(processor);
|
||||
}
|
||||
@@ -76,14 +77,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 +114,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();
|
||||
}
|
||||
@@ -135,7 +136,7 @@ bool OpenGLBackend::TimeIsCached(const TimeRange &time)
|
||||
|
||||
void OpenGLBackend::ThreadCompletedFrame(NodeDependency path, QByteArray hash, NodeValueTable table)
|
||||
{
|
||||
caching_ = false;
|
||||
SetWorkerBusyState(static_cast<RenderWorker*>(sender()), false);
|
||||
|
||||
QVariant value = table.Get(NodeParam::kTexture);
|
||||
OpenGLTexturePtr texture = value.value<OpenGLTexturePtr>();
|
||||
@@ -148,19 +149,14 @@ void OpenGLBackend::ThreadCompletedFrame(NodeDependency path, QByteArray hash, N
|
||||
QString cache_fn = frame_cache()->CachePathName(hash);
|
||||
|
||||
// Find an available worker to download this texture
|
||||
foreach (RenderWorker* worker, processors_) {
|
||||
// Check if one is available, but worst case if none of them are available, just queue it on the last worker since
|
||||
// it's the least likely to get work
|
||||
if (worker->IsAvailable() || worker == processors_.last()) {
|
||||
QMetaObject::invokeMethod(worker,
|
||||
"Download",
|
||||
Q_ARG(NodeDependency, path),
|
||||
Q_ARG(QByteArray, hash),
|
||||
Q_ARG(QVariant, QVariant::fromValue(texture)),
|
||||
Q_ARG(QString, cache_fn));
|
||||
break;
|
||||
}
|
||||
}
|
||||
QMetaObject::invokeMethod(processors_.at(last_download_thread_%processors_.size()),
|
||||
"Download",
|
||||
Q_ARG(NodeDependency, path),
|
||||
Q_ARG(QByteArray, hash),
|
||||
Q_ARG(QVariant, QVariant::fromValue(texture)),
|
||||
Q_ARG(QString, cache_fn));
|
||||
|
||||
last_download_thread_++;
|
||||
}
|
||||
|
||||
// Set as push texture
|
||||
@@ -168,6 +164,7 @@ void OpenGLBackend::ThreadCompletedFrame(NodeDependency path, QByteArray hash, N
|
||||
emit CachedFrameReady(path.in(), value);
|
||||
}
|
||||
|
||||
// Queue up a new frame for this worker
|
||||
CacheNext();
|
||||
}
|
||||
|
||||
@@ -175,18 +172,27 @@ 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()
|
||||
void OpenGLBackend::ThreadSkippedFrame(NodeDependency dep, QByteArray hash)
|
||||
{
|
||||
caching_ = false;
|
||||
frame_cache()->SetHash(dep.in(), hash);
|
||||
SetWorkerBusyState(static_cast<RenderWorker*>(sender()), false);
|
||||
|
||||
// Queue up a new frame for this worker
|
||||
CacheNext();
|
||||
}
|
||||
|
||||
void OpenGLBackend::ThreadHashAlreadyExists(NodeDependency dep, QByteArray hash)
|
||||
{
|
||||
ThreadCompletedDownload(dep, hash);
|
||||
SetWorkerBusyState(static_cast<RenderWorker*>(sender()), false);
|
||||
|
||||
ThreadSkippedFrame();
|
||||
// Queue up a new frame for this worker
|
||||
CacheNext();
|
||||
}
|
||||
|
||||
@@ -34,10 +34,12 @@ private:
|
||||
|
||||
OpenGLShaderCache shader_cache_;
|
||||
|
||||
int last_download_thread_;
|
||||
|
||||
private slots:
|
||||
void ThreadCompletedFrame(NodeDependency path, QByteArray hash, NodeValueTable table);
|
||||
void ThreadCompletedDownload(NodeDependency dep, QByteArray hash);
|
||||
void ThreadSkippedFrame();
|
||||
void ThreadSkippedFrame(NodeDependency dep, QByteArray hash);
|
||||
void ThreadHashAlreadyExists(NodeDependency dep, QByteArray hash);
|
||||
|
||||
};
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
#include "openglcolorprocessor.h"
|
||||
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLFunctions>
|
||||
|
||||
#include "functions.h"
|
||||
|
||||
void OpenGLColorProcessor::Enable(QOpenGLContext *context, bool alpha_is_associated)
|
||||
{
|
||||
if (IsEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
context_ = context;
|
||||
|
||||
pipeline_ = OpenGLShader::CreateOCIO(context_,
|
||||
ocio_lut_,
|
||||
GetProcessor(),
|
||||
alpha_is_associated);
|
||||
}
|
||||
|
||||
bool OpenGLColorProcessor::IsEnabled() const
|
||||
{
|
||||
return ocio_lut_;
|
||||
}
|
||||
|
||||
OpenGLShaderPtr OpenGLColorProcessor::pipeline() const
|
||||
{
|
||||
return pipeline_;
|
||||
}
|
||||
|
||||
void OpenGLColorProcessor::ProcessOpenGL()
|
||||
{
|
||||
olive::gl::OCIOBlit(pipeline_, ocio_lut_);
|
||||
}
|
||||
|
||||
OpenGLColorProcessor::OpenGLColorProcessor(const QString &source_space, const QString &dest_space) :
|
||||
ColorProcessor(source_space, dest_space),
|
||||
ocio_lut_(0)
|
||||
{
|
||||
}
|
||||
|
||||
OpenGLColorProcessor::OpenGLColorProcessor(const QString &source_space, QString display, QString view, const QString &look) :
|
||||
ColorProcessor(source_space, display, view, look),
|
||||
ocio_lut_(0)
|
||||
{
|
||||
}
|
||||
|
||||
OpenGLColorProcessor::~OpenGLColorProcessor()
|
||||
{
|
||||
if (IsEnabled()) {
|
||||
// Clean up OCIO LUT texture and shader
|
||||
context_->functions()->glDeleteTextures(1, &ocio_lut_);
|
||||
pipeline_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
OpenGLColorProcessorPtr OpenGLColorProcessor::CreateOpenGL(const QString &source_space, const QString &dest_space)
|
||||
{
|
||||
return std::make_shared<OpenGLColorProcessor>(source_space, dest_space);
|
||||
}
|
||||
|
||||
OpenGLColorProcessorPtr OpenGLColorProcessor::CreateOpenGL(const QString &source_space, const QString &display, const QString &view, const QString &look)
|
||||
{
|
||||
return std::make_shared<OpenGLColorProcessor>(source_space, display, view, look);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef OPENGLCOLORPROCESSOR_H
|
||||
#define OPENGLCOLORPROCESSOR_H
|
||||
|
||||
#include "openglshader.h"
|
||||
#include "render/colorprocessor.h"
|
||||
|
||||
class OpenGLColorProcessor;
|
||||
using OpenGLColorProcessorPtr = std::shared_ptr<OpenGLColorProcessor>;
|
||||
|
||||
class OpenGLColorProcessor : public ColorProcessor
|
||||
{
|
||||
public:
|
||||
OpenGLColorProcessor(const QString &source_space, const QString &dest_space);
|
||||
|
||||
OpenGLColorProcessor(const QString& source_space,
|
||||
QString display,
|
||||
QString view,
|
||||
const QString& look);
|
||||
|
||||
~OpenGLColorProcessor();
|
||||
|
||||
static OpenGLColorProcessorPtr CreateOpenGL(const QString& source_space, const QString& dest_space);
|
||||
|
||||
static OpenGLColorProcessorPtr CreateOpenGL(const QString& source_space,
|
||||
const QString& display,
|
||||
const QString& view,
|
||||
const QString& look);
|
||||
|
||||
void Enable(QOpenGLContext* context, bool alpha_is_associated);
|
||||
bool IsEnabled() const;
|
||||
|
||||
OpenGLShaderPtr pipeline() const;
|
||||
|
||||
void ProcessOpenGL();
|
||||
|
||||
private:
|
||||
QOpenGLContext* context_;
|
||||
|
||||
GLuint ocio_lut_;
|
||||
|
||||
OpenGLShaderPtr pipeline_;
|
||||
|
||||
};
|
||||
|
||||
#endif // OPENGLCOLORPROCESSOR_H
|
||||
@@ -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
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
#include "openglworker.h"
|
||||
|
||||
#include "core.h"
|
||||
#include "functions.h"
|
||||
#include "node/node.h"
|
||||
#include "openglcolorprocessor.h"
|
||||
#include "render/colormanager.h"
|
||||
#include "render/pixelservice.h"
|
||||
|
||||
OpenGLWorker::OpenGLWorker(QOpenGLContext *share_ctx, OpenGLShaderCache *shader_cache, DecoderCache *decoder_cache, VideoRenderFrameCache *frame_cache, QObject *parent) :
|
||||
VideoRenderWorker(decoder_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),
|
||||
@@ -47,18 +50,79 @@ bool OpenGLWorker::InitInternal()
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGLWorker::FrameToValue(FramePtr frame, NodeValueTable *table)
|
||||
void OpenGLWorker::FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable *table)
|
||||
{
|
||||
// Set up OCIO context
|
||||
OpenGLColorProcessorPtr color_processor = std::static_pointer_cast<OpenGLColorProcessor>(color_cache()->Get(stream.get()));
|
||||
|
||||
// Ensure stream is video or image type
|
||||
if (stream->type() != Stream::kVideo && stream->type() != Stream::kImage) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImageStreamPtr video_stream = std::static_pointer_cast<ImageStream>(stream);
|
||||
|
||||
if (!color_processor) {
|
||||
QString input_colorspace = video_stream->colorspace();
|
||||
if (input_colorspace.isEmpty()) {
|
||||
// FIXME: Should use Stream->Footage to find the Project* since that's a direct chain
|
||||
input_colorspace = olive::core.GetActiveProject()->default_input_colorspace();
|
||||
}
|
||||
|
||||
color_processor = OpenGLColorProcessor::CreateOpenGL(input_colorspace,
|
||||
OCIO::ROLE_SCENE_LINEAR);
|
||||
color_cache()->Add(stream.get(), color_processor);
|
||||
}
|
||||
|
||||
// OCIO's CPU conversion is more accurate, so for online we render on CPU but offline we render GPU
|
||||
if (video_params().mode() == olive::kOnline) {
|
||||
// If alpha is associated, disassociate for the color transform
|
||||
if (video_stream->premultiplied_alpha()) {
|
||||
ColorManager::DisassociateAlpha(frame);
|
||||
}
|
||||
|
||||
// Convert frame to float for OCIO
|
||||
frame = PixelService::ConvertPixelFormat(frame, olive::PIX_FMT_RGBA32F);
|
||||
|
||||
// Perform color transform
|
||||
color_processor->ConvertFrame(frame);
|
||||
|
||||
// Associate alpha
|
||||
if (video_stream->premultiplied_alpha()) {
|
||||
ColorManager::ReassociateAlpha(frame);
|
||||
} else {
|
||||
ColorManager::AssociateAlpha(frame);
|
||||
}
|
||||
}
|
||||
|
||||
OpenGLTexturePtr footage_tex = std::make_shared<OpenGLTexture>();
|
||||
footage_tex->Create(ctx_, frame);
|
||||
|
||||
// OCIO's CPU conversion is more accurate, so for online we render on CPU but offline we render GPU
|
||||
//if (video_params().mode() == olive::kOnline) {
|
||||
// Convert frame to float for
|
||||
//frame = PixelService::ConvertPixelFormat(frame, olive::PIX_FMT_RGBA32F);
|
||||
//}
|
||||
if (video_params().mode() == olive::kOffline) {
|
||||
if (!color_processor->IsEnabled()) {
|
||||
color_processor->Enable(ctx_, video_stream->premultiplied_alpha());
|
||||
}
|
||||
|
||||
// FIXME: Alpha association and color management
|
||||
// Create destination texture
|
||||
OpenGLTexturePtr associated_tex = std::make_shared<OpenGLTexture>();
|
||||
associated_tex->Create(ctx_, footage_tex->width(), footage_tex->height(), footage_tex->format());
|
||||
|
||||
// Set viewport for texture size
|
||||
functions_->glViewport(0, 0, footage_tex->width(), footage_tex->height());
|
||||
|
||||
buffer_.Attach(associated_tex);
|
||||
buffer_.Bind();
|
||||
footage_tex->Bind();
|
||||
|
||||
// Blit old texture to new texture through OCIO shader
|
||||
color_processor->ProcessOpenGL();
|
||||
|
||||
footage_tex->Release();
|
||||
buffer_.Release();
|
||||
buffer_.Detach();
|
||||
|
||||
footage_tex = associated_tex;
|
||||
}
|
||||
|
||||
table->Push(NodeParam::kTexture, QVariant::fromValue(footage_tex));
|
||||
}
|
||||
@@ -66,7 +130,6 @@ void OpenGLWorker::FrameToValue(FramePtr frame, NodeValueTable *table)
|
||||
void OpenGLWorker::CloseInternal()
|
||||
{
|
||||
buffer_.Destroy();
|
||||
|
||||
functions_ = nullptr;
|
||||
delete ctx_;
|
||||
}
|
||||
@@ -80,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;
|
||||
@@ -184,7 +247,10 @@ void OpenGLWorker::RunNodeAccelerated(Node *node, const NodeValueDatabase *input
|
||||
}
|
||||
}
|
||||
|
||||
//qDebug() << " Blitting with shader!";
|
||||
// Ensure viewport is correct
|
||||
functions_->glViewport(0, 0, video_params().effective_width(), video_params().effective_height());
|
||||
|
||||
// Blit this texture through this shader
|
||||
olive::gl::Blit(shader);
|
||||
|
||||
// Release any textures we bound before
|
||||
|
||||
@@ -13,7 +13,6 @@ class OpenGLWorker : public VideoRenderWorker {
|
||||
public:
|
||||
OpenGLWorker(QOpenGLContext* share_ctx,
|
||||
OpenGLShaderCache* shader_cache,
|
||||
DecoderCache* decoder_cache,
|
||||
VideoRenderFrameCache* frame_cache,
|
||||
QObject* parent = nullptr);
|
||||
|
||||
@@ -45,7 +44,7 @@ protected:
|
||||
|
||||
virtual void CloseInternal() override;
|
||||
|
||||
virtual void FrameToValue(FramePtr frame, NodeValueTable* table) override;
|
||||
virtual void FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable* table) override;
|
||||
|
||||
virtual void RunNodeAccelerated(Node *node, const NodeValueDatabase *input_params, NodeValueTable* output_params) override;
|
||||
|
||||
|
||||
@@ -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,50 @@ 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 (!WorkerIsBusy(worker)) {
|
||||
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;
|
||||
|
||||
SetWorkerBusyState(worker, true);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ViewerOutput *RenderBackend::viewer_node() const
|
||||
@@ -244,11 +265,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 +290,27 @@ void RenderBackend::UpdateNodeInputs()
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderBackend::WorkerIsBusy(RenderWorker *worker) const
|
||||
{
|
||||
return processor_busy_state_.at(processors_.indexOf(worker));
|
||||
}
|
||||
|
||||
void RenderBackend::SetWorkerBusyState(RenderWorker *worker, bool busy)
|
||||
{
|
||||
processor_busy_state_.replace(processors_.indexOf(worker), busy);
|
||||
}
|
||||
|
||||
bool RenderBackend::AllProcessorsAreAvailable() const
|
||||
{
|
||||
foreach (bool busy, processor_busy_state_) {
|
||||
if (busy) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const QVector<QThread *> &RenderBackend::threads()
|
||||
{
|
||||
return threads_;
|
||||
@@ -291,7 +328,6 @@ void RenderBackend::InitWorkers()
|
||||
QThread* thread = threads().at(i);
|
||||
|
||||
// Connect to it
|
||||
connect(processor, SIGNAL(RequestSibling(NodeDependency)), this, SLOT(ThreadRequestedSibling(NodeDependency)));
|
||||
ConnectWorkerToThis(processor);
|
||||
|
||||
// Finally, we can move it to its own thread
|
||||
@@ -300,20 +336,9 @@ void RenderBackend::InitWorkers()
|
||||
// This function blocks the main thread intentionally. See the documentation for this function to see why.
|
||||
processor->Init();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderBackend::ThreadRequestedSibling(NodeDependency dep)
|
||||
{
|
||||
// Try to queue another thread to run this dep in advance
|
||||
foreach (RenderWorker* worker, processors_) {
|
||||
if (worker->IsAvailable()) {
|
||||
QMetaObject::invokeMethod(worker,
|
||||
"RenderAsSibling",
|
||||
Qt::QueuedConnection,
|
||||
Q_ARG(NodeDependency, dep));
|
||||
return;
|
||||
}
|
||||
}
|
||||
processor_busy_state_.resize(processors_.size());
|
||||
processor_busy_state_.fill(false);
|
||||
}
|
||||
|
||||
void RenderBackend::QueueRecompile()
|
||||
|
||||
@@ -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,23 +80,24 @@ protected:
|
||||
|
||||
bool ViewerIsConnected() const;
|
||||
|
||||
DecoderCache* decoder_cache();
|
||||
|
||||
const QString& cache_id() const;
|
||||
|
||||
void QueueValueUpdate(const TimeRange& range);
|
||||
|
||||
void UpdateNodeInputs();
|
||||
|
||||
bool WorkerIsBusy(RenderWorker* worker) const;
|
||||
void SetWorkerBusyState(RenderWorker* worker, bool busy);
|
||||
|
||||
QList<TimeRange> cache_queue_;
|
||||
|
||||
QVector<RenderWorker*> processors_;
|
||||
|
||||
bool compiled_;
|
||||
|
||||
bool caching_;
|
||||
|
||||
private:
|
||||
bool AllProcessorsAreAvailable() const;
|
||||
|
||||
/**
|
||||
* @brief Internal list of RenderProcessThreads
|
||||
*/
|
||||
@@ -124,8 +123,6 @@ private:
|
||||
*/
|
||||
QString error_;
|
||||
|
||||
DecoderCache decoder_cache_;
|
||||
|
||||
QString cache_name_;
|
||||
qint64 cache_time_;
|
||||
QString cache_id_;
|
||||
@@ -137,10 +134,11 @@ private:
|
||||
TimeRange value_update_range_;
|
||||
|
||||
bool recompile_queued_;
|
||||
bool input_update_queued_;
|
||||
|
||||
QVector<bool> processor_busy_state_;
|
||||
|
||||
private slots:
|
||||
void ThreadRequestedSibling(NodeDependency dep);
|
||||
|
||||
void QueueRecompile();
|
||||
|
||||
};
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifndef RENDERCACHE_H
|
||||
#define RENDERCACHE_H
|
||||
|
||||
#include <QMap>
|
||||
#include <QMutex>
|
||||
|
||||
template<class K, class V>
|
||||
class RenderCache
|
||||
{
|
||||
public:
|
||||
RenderCache() = default;
|
||||
|
||||
void Clear(){values_.clear();}
|
||||
|
||||
void Add(K key, V val){values_.insert(key, val);}
|
||||
|
||||
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,19 +4,12 @@
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
bool RenderWorker::Init()
|
||||
{
|
||||
if (started_) {
|
||||
@@ -34,6 +27,8 @@ void RenderWorker::Close()
|
||||
{
|
||||
CloseInternal();
|
||||
|
||||
decoder_cache_.Clear();
|
||||
|
||||
started_ = false;
|
||||
}
|
||||
|
||||
@@ -50,9 +45,6 @@ NodeValueTable RenderWorker::RenderAsSibling(NodeDependency dep)
|
||||
|
||||
//qDebug() << "Processing" << node->id();
|
||||
|
||||
// Set working state
|
||||
working_++;
|
||||
|
||||
// Firstly we check if this node is a "Block", if it is that means it's part of a linked list of mutually exclusive
|
||||
// nodes based on time and we might need to locate which Block to attach to
|
||||
if (node->IsTrack()) {
|
||||
@@ -64,17 +56,9 @@ NodeValueTable RenderWorker::RenderAsSibling(NodeDependency dep)
|
||||
|
||||
// We're done!
|
||||
|
||||
// End this working state
|
||||
working_--;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
DecoderCache *RenderWorker::decoder_cache()
|
||||
{
|
||||
return decoder_cache_;
|
||||
}
|
||||
|
||||
NodeValueTable RenderWorker::RenderInternal(const NodeDependency &path)
|
||||
{
|
||||
return RenderAsSibling(path);
|
||||
@@ -98,17 +82,16 @@ StreamPtr RenderWorker::ResolveStreamFromInput(NodeInput *input)
|
||||
return input->get_value_at_time(0).value<StreamPtr>();
|
||||
}
|
||||
|
||||
DecoderPtr RenderWorker::ResolveDecoderFromInput(NodeInput *input)
|
||||
DecoderPtr RenderWorker::ResolveDecoderFromInput(StreamPtr stream)
|
||||
{
|
||||
// Access a map of Node inputs and decoder instances and retrieve a frame!
|
||||
StreamPtr stream = ResolveStreamFromInput(input);
|
||||
DecoderPtr decoder = decoder_cache()->GetDecoder(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()->AddDecoder(stream.get(), decoder);
|
||||
decoder_cache_.Add(stream.get(), decoder);
|
||||
}
|
||||
|
||||
return decoder;
|
||||
@@ -146,13 +129,17 @@ NodeValueTable RenderWorker::ProcessNodeNormally(const NodeDependency& dep)
|
||||
|
||||
// Exception for Footage types where we actually retrieve some Footage data from a decoder
|
||||
if (input->data_type() == NodeParam::kFootage) {
|
||||
DecoderPtr decoder = ResolveDecoderFromInput(input);
|
||||
StreamPtr stream = ResolveStreamFromInput(input);
|
||||
|
||||
if (decoder) {
|
||||
FramePtr frame = RetrieveFromDecoder(decoder, input_time);
|
||||
if (stream) {
|
||||
DecoderPtr decoder = ResolveDecoderFromInput(stream);
|
||||
|
||||
if (frame) {
|
||||
FrameToValue(frame, &table);
|
||||
if (decoder) {
|
||||
FramePtr frame = RetrieveFromDecoder(decoder, input_time);
|
||||
|
||||
if (frame) {
|
||||
FrameToValue(stream, frame, &table);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -20,8 +20,6 @@ public:
|
||||
|
||||
bool IsStarted();
|
||||
|
||||
bool IsAvailable();
|
||||
|
||||
public slots:
|
||||
void Close();
|
||||
|
||||
@@ -30,8 +28,6 @@ public slots:
|
||||
NodeValueTable RenderAsSibling(NodeDependency dep);
|
||||
|
||||
signals:
|
||||
void RequestSibling(NodeDependency path);
|
||||
|
||||
void CompletedCache(NodeDependency dep, NodeValueTable data);
|
||||
|
||||
protected:
|
||||
@@ -46,24 +42,20 @@ protected:
|
||||
virtual void RunNodeAccelerated(Node *node, const NodeValueDatabase *input_params, NodeValueTable* output_params);
|
||||
|
||||
StreamPtr ResolveStreamFromInput(NodeInput* input);
|
||||
DecoderPtr ResolveDecoderFromInput(NodeInput* input);
|
||||
DecoderPtr ResolveDecoderFromInput(StreamPtr stream);
|
||||
|
||||
virtual FramePtr RetrieveFromDecoder(DecoderPtr decoder, const TimeRange& range) = 0;
|
||||
|
||||
virtual void FrameToValue(FramePtr frame, NodeValueTable* table) = 0;
|
||||
virtual void FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable* table) = 0;
|
||||
|
||||
NodeValueTable ProcessNodeNormally(const NodeDependency &dep);
|
||||
|
||||
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);
|
||||
@@ -175,7 +177,7 @@ void VideoRenderBackend::CacheIDChangedEvent(const QString &id)
|
||||
void VideoRenderBackend::ConnectWorkerToThis(RenderWorker *processor)
|
||||
{
|
||||
connect(processor, SIGNAL(CompletedFrame(NodeDependency, QByteArray, NodeValueTable)), this, SLOT(ThreadCompletedFrame(NodeDependency, QByteArray, NodeValueTable)));
|
||||
connect(processor, SIGNAL(HashAlreadyBeingCached()), this, SLOT(ThreadSkippedFrame()));
|
||||
connect(processor, SIGNAL(HashAlreadyBeingCached(NodeDependency, QByteArray)), this, SLOT(ThreadSkippedFrame(NodeDependency, QByteArray)));
|
||||
connect(processor, SIGNAL(CompletedDownload(NodeDependency, QByteArray)), this, SLOT(ThreadCompletedDownload(NodeDependency, QByteArray)));
|
||||
connect(processor, SIGNAL(HashAlreadyExists(NodeDependency, QByteArray)), this, SLOT(ThreadHashAlreadyExists(NodeDependency, QByteArray)));
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <QLinkedList>
|
||||
|
||||
#include "colorprocessorcache.h"
|
||||
#include "node/output/viewer/viewer.h"
|
||||
#include "renderbackend.h"
|
||||
#include "render/pixelformat.h"
|
||||
|
||||
@@ -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,8 +4,8 @@
|
||||
#include "node/node.h"
|
||||
#include "render/pixelservice.h"
|
||||
|
||||
VideoRenderWorker::VideoRenderWorker(DecoderCache *decoder_cache, VideoRenderFrameCache *frame_cache, QObject *parent) :
|
||||
RenderWorker(decoder_cache, parent),
|
||||
VideoRenderWorker::VideoRenderWorker(VideoRenderFrameCache *frame_cache, QObject *parent) :
|
||||
RenderWorker(parent),
|
||||
frame_cache_(frame_cache)
|
||||
{
|
||||
|
||||
@@ -36,7 +36,7 @@ NodeValueTable VideoRenderWorker::RenderInternal(const NodeDependency& path)
|
||||
emit CompletedFrame(path, hash, value);
|
||||
} else {
|
||||
// Another thread must be caching this already, nothing to be done
|
||||
emit HashAlreadyBeingCached();
|
||||
emit HashAlreadyBeingCached(path, hash);
|
||||
}
|
||||
|
||||
return value;
|
||||
@@ -85,7 +85,7 @@ void VideoRenderWorker::HashNodeRecursively(QCryptographicHash *hash, Node* n, c
|
||||
// We have one exception for FOOTAGE types, since we resolve the footage into a frame in the renderer
|
||||
if (input->data_type() == NodeParam::kFootage) {
|
||||
StreamPtr stream = ResolveStreamFromInput(input);
|
||||
DecoderPtr decoder = ResolveDecoderFromInput(input);
|
||||
DecoderPtr decoder = ResolveDecoderFromInput(stream);
|
||||
|
||||
if (decoder != nullptr) {
|
||||
// Add footage details to hash
|
||||
@@ -99,10 +99,19 @@ void VideoRenderWorker::HashNodeRecursively(QCryptographicHash *hash, Node* n, c
|
||||
// Footage stream
|
||||
hash->addData(QString::number(stream->index()).toUtf8());
|
||||
|
||||
// Footage timestamp
|
||||
hash->addData(QString::number(decoder->GetTimestampFromTime(time)).toUtf8());
|
||||
if (stream->type() == Stream::kImage || stream->type() == Stream::kVideo) {
|
||||
ImageStreamPtr video_stream = std::static_pointer_cast<ImageStream>(stream);
|
||||
|
||||
// FIXME: Add colorspace and alpha assoc
|
||||
// Footage timestamp
|
||||
hash->addData(QString::number(decoder->GetTimestampFromTime(time)).toUtf8());
|
||||
|
||||
// Current colorspace
|
||||
// FIXME: Handle empty colorspace...
|
||||
hash->addData(video_stream->colorspace().toUtf8());
|
||||
|
||||
// Alpha associated setting
|
||||
hash->addData(QString::number(video_stream->premultiplied_alpha()).toUtf8());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,8 +139,6 @@ void VideoRenderWorker::CloseInternal()
|
||||
|
||||
void VideoRenderWorker::Download(NodeDependency dep, QByteArray hash, QVariant texture, QString filename)
|
||||
{
|
||||
working_++;
|
||||
|
||||
PixelFormatInfo format_info = PixelService::GetPixelFormatInfo(video_params().format());
|
||||
|
||||
// Set up OIIO::ImageSpec for compressing cached images on disk
|
||||
@@ -153,8 +160,6 @@ void VideoRenderWorker::Download(NodeDependency dep, QByteArray hash, QVariant t
|
||||
} else {
|
||||
qWarning() << "Failed to open output file:" << filename;
|
||||
}
|
||||
|
||||
working_--;
|
||||
}
|
||||
|
||||
NodeValueTable VideoRenderWorker::RenderBlock(TrackOutput *track, const TimeRange &range)
|
||||
@@ -171,3 +176,8 @@ NodeValueTable VideoRenderWorker::RenderBlock(TrackOutput *track, const TimeRang
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
ColorProcessorCache *VideoRenderWorker::color_cache()
|
||||
{
|
||||
return &color_cache_;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <QCryptographicHash>
|
||||
|
||||
#include "colorprocessorcache.h"
|
||||
#include "node/dependency.h"
|
||||
#include "render/videoparams.h"
|
||||
#include "renderworker.h"
|
||||
@@ -11,7 +12,7 @@
|
||||
class VideoRenderWorker : public RenderWorker {
|
||||
Q_OBJECT
|
||||
public:
|
||||
VideoRenderWorker(DecoderCache* decoder_cache, VideoRenderFrameCache* frame_cache, QObject* parent = nullptr);
|
||||
VideoRenderWorker(VideoRenderFrameCache* frame_cache, QObject* parent = nullptr);
|
||||
|
||||
void SetParameters(const VideoRenderingParams& video_params);
|
||||
|
||||
@@ -23,7 +24,7 @@ signals:
|
||||
|
||||
void CompletedDownload(NodeDependency path, QByteArray hash);
|
||||
|
||||
void HashAlreadyBeingCached();
|
||||
void HashAlreadyBeingCached(NodeDependency path, QByteArray hash);
|
||||
|
||||
void HashAlreadyExists(NodeDependency path, QByteArray hash);
|
||||
|
||||
@@ -44,6 +45,8 @@ protected:
|
||||
|
||||
virtual NodeValueTable RenderBlock(TrackOutput *track, const TimeRange& range) override;
|
||||
|
||||
ColorProcessorCache* color_cache();
|
||||
|
||||
private:
|
||||
void ProcessNode();
|
||||
|
||||
@@ -53,6 +56,8 @@ private:
|
||||
|
||||
VideoRenderFrameCache* frame_cache_;
|
||||
|
||||
ColorProcessorCache color_cache_;
|
||||
|
||||
QByteArray download_buffer_;
|
||||
|
||||
private slots:
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
#include <OpenColorIO/OpenColorIO.h>
|
||||
namespace OCIO = OCIO_NAMESPACE::v1;
|
||||
|
||||
#include "common/constructors.h"
|
||||
#include "decoder/frame.h"
|
||||
|
||||
|
||||
class ColorProcessor;
|
||||
using ColorProcessorPtr = std::shared_ptr<ColorProcessor>;
|
||||
|
||||
@@ -19,6 +21,8 @@ public:
|
||||
QString view,
|
||||
const QString& look);
|
||||
|
||||
DISABLE_COPY_MOVE(ColorProcessor)
|
||||
|
||||
static ColorProcessorPtr Create(const QString& source_space, const QString& dest_space);
|
||||
|
||||
static ColorProcessorPtr Create(const QString& source_space,
|
||||
|
||||
@@ -239,7 +239,7 @@ void TimelineWidget::PointerTool::InitiateDrag(const TimelineCoordinate &mouse_p
|
||||
olive::timeline::MovementMode trim_mode = olive::timeline::kNone;
|
||||
|
||||
// FIXME: Hardcoded number
|
||||
const int kTrimHandle = 20;
|
||||
const int kTrimHandle = 10;
|
||||
|
||||
qreal mouse_x = parent()->TimeToScene(mouse_pos.GetFrame());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user