shift renderer prototype to new node structure
This commit is contained in:
@@ -327,6 +327,7 @@ void Core::AddOpenProject(ProjectPtr p)
|
||||
void Core::DeclareTypesForQt()
|
||||
{
|
||||
qRegisterMetaType<Task::Status>("Task::Status");
|
||||
qRegisterMetaType<NodeDependency>();
|
||||
}
|
||||
|
||||
void Core::StartGUI(bool full_screen)
|
||||
|
||||
@@ -23,6 +23,8 @@ add_subdirectory(processor)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/dependency.h
|
||||
node/dependency.cpp
|
||||
node/edge.h
|
||||
node/edge.cpp
|
||||
node/graph.h
|
||||
|
||||
@@ -18,22 +18,25 @@
|
||||
|
||||
***/
|
||||
|
||||
#ifndef RENDERERPROBE_H
|
||||
#define RENDERERPROBE_H
|
||||
#include "dependency.h"
|
||||
|
||||
#include "node/node.h"
|
||||
#include "renderpath.h"
|
||||
|
||||
class RendererProbe
|
||||
NodeDependency::NodeDependency() :
|
||||
node_(nullptr)
|
||||
{
|
||||
public:
|
||||
RendererProbe();
|
||||
}
|
||||
|
||||
static RenderPath ProbeNode(Node* node, int thread_count, const rational &time);
|
||||
NodeDependency::NodeDependency(NodeOutput *node, const rational &time) :
|
||||
node_(node),
|
||||
time_(time)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
static void TraverseNode(RenderPath& path, Node* node, const rational& time, int thread, int index);
|
||||
NodeOutput *NodeDependency::node() const
|
||||
{
|
||||
return node_;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // RENDERERPROBE_H
|
||||
const rational& NodeDependency::time() const
|
||||
{
|
||||
return time_;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef NODEDEPENDENCY_H
|
||||
#define NODEDEPENDENCY_H
|
||||
|
||||
#include <QMetaType>
|
||||
|
||||
#include "common/rational.h"
|
||||
#include "node/output.h"
|
||||
|
||||
class NodeDependency {
|
||||
public:
|
||||
NodeDependency();
|
||||
NodeDependency(NodeOutput* node, const rational& time);
|
||||
|
||||
NodeOutput* node() const;
|
||||
const rational& time() const;
|
||||
|
||||
private:
|
||||
NodeOutput* node_;
|
||||
rational time_;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(NodeDependency)
|
||||
|
||||
#endif // NODEDEPENDENCY_H
|
||||
+34
-7
@@ -24,7 +24,6 @@
|
||||
|
||||
NodeInput::NodeInput(const QString& id) :
|
||||
NodeParam(id),
|
||||
time_(-1),
|
||||
keyframing_(false)
|
||||
{
|
||||
// Have at least one keyframe/value active at any time
|
||||
@@ -51,23 +50,51 @@ bool NodeInput::can_accept_type(const NodeParam::DataType &data_type)
|
||||
return AreDataTypesCompatible(data_type, inputs_);
|
||||
}
|
||||
|
||||
NodeOutput *NodeInput::get_connected_output()
|
||||
{
|
||||
if (!edges_.isEmpty()) {
|
||||
return edges_.first()->output();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Node *NodeInput::get_connected_node()
|
||||
{
|
||||
NodeOutput* output = get_connected_output();
|
||||
|
||||
if (output != nullptr) {
|
||||
return output->parent();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QVariant NodeInput::get_value(const rational& time)
|
||||
{
|
||||
QVariant v;
|
||||
|
||||
lock_.lock();
|
||||
|
||||
if (time_ != time) {
|
||||
// Retrieve the value
|
||||
if (!edges_.isEmpty()) {
|
||||
// One connection - use the output of the connected Node
|
||||
// A connection - use the output of the connected Node
|
||||
value_ = edges_.first()->output()->get_value(time);
|
||||
} else {
|
||||
// No connections - use the internal value
|
||||
// FIXME: Re-implement keyframing
|
||||
value_ = keyframes_.first().value();
|
||||
}
|
||||
|
||||
// No connections - use the internal value
|
||||
// FIXME: Re-implement keyframing
|
||||
value_ = keyframes_.first().value();
|
||||
|
||||
time_ = time;
|
||||
}
|
||||
|
||||
return value_;
|
||||
v = value_;
|
||||
|
||||
lock_.unlock();
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void NodeInput::set_value(const QVariant &value)
|
||||
|
||||
+18
-10
@@ -54,6 +54,24 @@ public:
|
||||
*/
|
||||
bool can_accept_type(const DataType& data_type);
|
||||
|
||||
/**
|
||||
* @brief If this input is connected to an output, retrieve the output parameter
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* The output parameter if connected or nullptr if not
|
||||
*/
|
||||
NodeOutput* get_connected_output();
|
||||
|
||||
/**
|
||||
* @brief If this input is connected to an output, retrieve the Node whose output is connected
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* The connected Node if connected or nullptr if not
|
||||
*/
|
||||
Node* get_connected_node();
|
||||
|
||||
/**
|
||||
* @brief Get the value at a given time
|
||||
*
|
||||
@@ -109,16 +127,6 @@ private:
|
||||
*/
|
||||
QList<NodeKeyframe> keyframes_;
|
||||
|
||||
/**
|
||||
* @brief Currently cached value
|
||||
*/
|
||||
QVariant value_;
|
||||
|
||||
/**
|
||||
* @brief Last timecode that a value was requested with
|
||||
*/
|
||||
rational time_;
|
||||
|
||||
/**
|
||||
* @brief Internal keyframing enabled setting
|
||||
*/
|
||||
|
||||
+10
-19
@@ -232,11 +232,18 @@ QList<NodeDependency> Node::RunDependencies(NodeOutput *output, const rational &
|
||||
{
|
||||
Q_UNUSED(output)
|
||||
|
||||
QList<Node*> immediate_deps = GetImmediateDependencies();
|
||||
QList<NodeParam*> params = parameters();
|
||||
QList<NodeDependency> run_deps;
|
||||
|
||||
foreach (Node* dep, immediate_deps) {
|
||||
run_deps.append(NodeDependency(dep, time));
|
||||
foreach (NodeParam* p, params) {
|
||||
if (p->type() == NodeParam::kInput) {
|
||||
NodeOutput* potential_dep = static_cast<NodeInput*>(p)->get_connected_output();
|
||||
|
||||
if (potential_dep != nullptr) {
|
||||
run_deps.append(NodeDependency(potential_dep, time));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return run_deps;
|
||||
@@ -280,19 +287,3 @@ bool Node::HasParamWithID(const QString &id)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NodeDependency::NodeDependency(Node *node, const rational &time) :
|
||||
node_(node),
|
||||
time_(time)
|
||||
{
|
||||
}
|
||||
|
||||
Node *NodeDependency::node()
|
||||
{
|
||||
return node_;
|
||||
}
|
||||
|
||||
rational NodeDependency::time()
|
||||
{
|
||||
return time_;
|
||||
}
|
||||
|
||||
+1
-12
@@ -25,21 +25,10 @@
|
||||
#include <QObject>
|
||||
|
||||
#include "common/rational.h"
|
||||
#include "node/dependency.h"
|
||||
#include "node/input.h"
|
||||
#include "node/output.h"
|
||||
|
||||
class NodeDependency {
|
||||
public:
|
||||
NodeDependency(Node* node, const rational& time);
|
||||
|
||||
Node* node();
|
||||
rational time();
|
||||
|
||||
private:
|
||||
Node* node_;
|
||||
rational time_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A single processing unit that can be connected with others to create intricate processing systems
|
||||
*
|
||||
|
||||
+12
-5
@@ -23,8 +23,7 @@
|
||||
#include "node/node.h"
|
||||
|
||||
NodeOutput::NodeOutput(const QString &id) :
|
||||
NodeParam(id),
|
||||
time_(-1)
|
||||
NodeParam(id)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -48,8 +47,12 @@ void NodeOutput::set_data_type(const NodeParam::DataType &type)
|
||||
}
|
||||
}
|
||||
|
||||
const QVariant &NodeOutput::get_value(const rational& time)
|
||||
QVariant NodeOutput::get_value(const rational& time)
|
||||
{
|
||||
QVariant v;
|
||||
|
||||
lock_.lock();
|
||||
|
||||
if (time_ != time) {
|
||||
// Update the value
|
||||
value_ = parent()->Run(this, time);
|
||||
@@ -57,6 +60,10 @@ const QVariant &NodeOutput::get_value(const rational& time)
|
||||
time_ = time;
|
||||
}
|
||||
|
||||
// The value should be have been set by this point
|
||||
return value_;
|
||||
v = value_;
|
||||
|
||||
lock_.unlock();
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
+1
-4
@@ -61,14 +61,11 @@ public:
|
||||
* In many cases for efficiency, the Node can also ignore this request if it knows the output data will not change
|
||||
* (i.e. if the time has not changed from the last Process()).
|
||||
*/
|
||||
virtual const QVariant& get_value(const rational &time);
|
||||
virtual QVariant get_value(const rational &time);
|
||||
|
||||
private:
|
||||
DataType data_type_;
|
||||
|
||||
QVariant value_;
|
||||
|
||||
rational time_;
|
||||
};
|
||||
|
||||
#endif // NODEOUTPUT_H
|
||||
|
||||
@@ -107,7 +107,7 @@ QList<NodeDependency> TrackOutput::RunDependencies(NodeOutput* output, const rat
|
||||
ValidateCurrentBlock(time);
|
||||
|
||||
if (current_block_ != this) {
|
||||
deps.append(NodeDependency(current_block_, time));
|
||||
deps.append(NodeDependency(current_block_->texture_output(), time));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
|
||||
#include "viewer.h"
|
||||
|
||||
#include "render/rendertexture.h"
|
||||
|
||||
ViewerOutput::ViewerOutput() :
|
||||
attached_viewer_(nullptr)
|
||||
{
|
||||
@@ -83,11 +81,17 @@ void ViewerOutput::AttachViewer(ViewerPanel *viewer)
|
||||
void ViewerOutput::InvalidateCache(const rational &start_range, const rational &end_range)
|
||||
{
|
||||
// Update any attached viewer
|
||||
ViewerTimeChanged(current_time_);
|
||||
UpdateViewer();
|
||||
|
||||
Node::InvalidateCache(start_range, end_range);
|
||||
}
|
||||
|
||||
void ViewerOutput::UpdateViewer()
|
||||
{
|
||||
texture_input_->ClearCachedValue();
|
||||
ViewerTimeChanged(current_time_);
|
||||
}
|
||||
|
||||
QVariant ViewerOutput::Value(NodeOutput *output, const rational &time)
|
||||
{
|
||||
Q_UNUSED(output)
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "node/node.h"
|
||||
#include "panel/viewer/viewer.h"
|
||||
#include "render/rendertexture.h"
|
||||
|
||||
/**
|
||||
* @brief A bridge between a node system and a ViewerPanel
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "node/output.h"
|
||||
|
||||
NodeParam::NodeParam(const QString &id) :
|
||||
time_(-1),
|
||||
id_(id)
|
||||
{
|
||||
Q_ASSERT(!id_.isEmpty());
|
||||
@@ -151,6 +152,8 @@ NodeEdgePtr NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input)
|
||||
output->edges_.append(edge);
|
||||
input->edges_.append(edge);
|
||||
|
||||
input->ClearCachedValue();
|
||||
|
||||
// Emit a signal than an edge was added (only one signal needs emitting)
|
||||
emit input->EdgeAdded(edge);
|
||||
|
||||
@@ -165,6 +168,8 @@ void NodeParam::DisconnectEdge(NodeEdgePtr edge)
|
||||
output->edges_.removeAll(edge);
|
||||
input->edges_.removeAll(edge);
|
||||
|
||||
input->ClearCachedValue();
|
||||
|
||||
emit input->EdgeRemoved(edge);
|
||||
}
|
||||
|
||||
@@ -214,3 +219,10 @@ QString NodeParam::GetDefaultDataTypeName(const DataType& type)
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
void NodeParam::ClearCachedValue()
|
||||
{
|
||||
// Since get_value() will (read: should) never receive a negative number, this will effectively invalidate any value
|
||||
// currently cached
|
||||
time_ = -1;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#ifndef NODEPARAM_H
|
||||
#define NODEPARAM_H
|
||||
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
#include <QVector>
|
||||
@@ -202,6 +203,11 @@ public:
|
||||
*/
|
||||
static QString GetDefaultDataTypeName(const DataType &type);
|
||||
|
||||
/**
|
||||
* @brief Clear the cached value
|
||||
*/
|
||||
void ClearCachedValue();
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Signal emitted when an edge is added to this parameter
|
||||
@@ -225,6 +231,21 @@ protected:
|
||||
*/
|
||||
QVector<NodeEdgePtr> edges_;
|
||||
|
||||
/**
|
||||
* @brief Used for thread safety
|
||||
*/
|
||||
QMutex lock_;
|
||||
|
||||
/**
|
||||
* @brief Currently cached value
|
||||
*/
|
||||
QVariant value_;
|
||||
|
||||
/**
|
||||
* @brief Last timecode that a value was requested with
|
||||
*/
|
||||
rational time_;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Internal name string
|
||||
|
||||
@@ -16,12 +16,8 @@
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/processor/renderer/renderpath.h
|
||||
node/processor/renderer/renderpath.cpp
|
||||
node/processor/renderer/renderer.h
|
||||
node/processor/renderer/renderer.cpp
|
||||
node/processor/renderer/rendererprobe.h
|
||||
node/processor/renderer/rendererprobe.cpp
|
||||
node/processor/renderer/rendererthread.h
|
||||
node/processor/renderer/rendererthread.cpp
|
||||
PARENT_SCOPE
|
||||
|
||||
@@ -27,9 +27,6 @@
|
||||
#include <QImage>
|
||||
#include <QtMath>
|
||||
|
||||
#include "renderpath.h"
|
||||
#include "rendererprobe.h"
|
||||
|
||||
RendererProcessor::RendererProcessor() :
|
||||
started_(false),
|
||||
width_(0),
|
||||
@@ -208,6 +205,7 @@ void RendererProcessor::Start()
|
||||
// Ensure this connection is "Queued" so that it always runs in this object's threaded rather than any of the
|
||||
// other threads
|
||||
connect(threads_[i].get(), SIGNAL(FinishedPath()), this, SLOT(ThreadCallback()), Qt::QueuedConnection);
|
||||
connect(threads_[i].get(), SIGNAL(RequestSibling(NodeDependency)), this, SLOT(ThreadRequestSibling(NodeDependency)), Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
started_ = true;
|
||||
@@ -259,44 +257,11 @@ void RendererProcessor::CacheNext()
|
||||
|
||||
qDebug() << "Caching" << time_to_cache.toDouble();
|
||||
|
||||
Node* node_to_cache = texture_input_->edges().first()->output()->parent();
|
||||
|
||||
// Set graph time
|
||||
//node_to_cache->set_time(time_to_cache);
|
||||
|
||||
// Run this probe in another thread
|
||||
RenderPath path = RendererProbe::ProbeNode(node_to_cache, threads_.size(), time_to_cache);
|
||||
|
||||
cache_return_count_ = 0;
|
||||
|
||||
for (int i=0;i<threads_.size();i++) {
|
||||
threads_.at(i)->Queue(path.GetThreadPath(i), time_to_cache);
|
||||
}
|
||||
master_thread_ = threads_.at(0).get();
|
||||
master_thread_->Queue(NodeDependency(texture_input_->get_connected_output(), time_to_cache), true);
|
||||
|
||||
caching_ = true;
|
||||
|
||||
/*
|
||||
|
||||
|
||||
bool caching = false;
|
||||
|
||||
// Look for a thread that's available
|
||||
for (int i=0;i<threads_.size();i++) {
|
||||
RendererThreadPtr thread = threads_.at(i);
|
||||
|
||||
if (thread->Queue(node_to_cache, time_to_cache)) {
|
||||
// This thread is free and we've just taken control of it
|
||||
caching = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (caching) {
|
||||
cache_queue_.removeFirst();
|
||||
|
||||
qDebug() << "[RendererProcessor] Ready to cache" << time_to_cache.numerator() << "/" << time_to_cache.denominator();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// FIXME: Test code only
|
||||
@@ -305,21 +270,26 @@ void RendererProcessor::CacheNext()
|
||||
|
||||
void RendererProcessor::ThreadCallback()
|
||||
{
|
||||
cache_return_count_++;
|
||||
|
||||
if (cache_return_count_ == threads_.size()) {
|
||||
if (sender() == master_thread_) {
|
||||
// Threads are all done now, time to proceed
|
||||
caching_ = false;
|
||||
|
||||
// FIXME: Test code only
|
||||
// Signal update to viewer
|
||||
static_cast<ViewerOutput*>(texture_output()->edges().first()->input()->parent())->InvalidateCache(0, 0);
|
||||
// End test code
|
||||
// FIXME: Save the texture results here
|
||||
|
||||
CacheNext();
|
||||
}
|
||||
}
|
||||
|
||||
void RendererProcessor::ThreadRequestSibling(NodeDependency dep)
|
||||
{
|
||||
// Try to queue another thread to run this dep in advance
|
||||
for (int i=0;i<threads_.size();i++) {
|
||||
if (threads_.at(i)->Queue(dep, false)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RendererThread* RendererProcessor::CurrentThread()
|
||||
{
|
||||
return dynamic_cast<RendererThread*>(QThread::currentThread());
|
||||
|
||||
@@ -149,11 +149,13 @@ private:
|
||||
qint64 cache_time_;
|
||||
QString cache_id_;
|
||||
bool caching_;
|
||||
int cache_return_count_;
|
||||
RendererThread* master_thread_;
|
||||
|
||||
private slots:
|
||||
void ThreadCallback();
|
||||
|
||||
void ThreadRequestSibling(NodeDependency dep);
|
||||
|
||||
};
|
||||
|
||||
#endif // RENDERER_H
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "rendererprobe.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
RendererProbe::RendererProbe()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
RenderPath RendererProbe::ProbeNode(Node *node, int thread_count, const rational& time)
|
||||
{
|
||||
Q_ASSERT(thread_count > 0);
|
||||
|
||||
RenderPath render_path(thread_count);
|
||||
|
||||
TraverseNode(render_path, node, time, 0, 0);
|
||||
|
||||
render_path.Finalize();
|
||||
|
||||
return render_path;
|
||||
}
|
||||
|
||||
void RendererProbe::TraverseNode(RenderPath& path, Node *node, const rational& time, int thread, int index)
|
||||
{
|
||||
Q_UNUSED(path)
|
||||
Q_UNUSED(node)
|
||||
Q_UNUSED(time)
|
||||
Q_UNUSED(thread)
|
||||
Q_UNUSED(index)
|
||||
/*bool can_take_node = true;
|
||||
|
||||
if (path.ContainsNode(node) >= 0) {
|
||||
if (path.NodeIndex(node) < index) {
|
||||
// If the other thread's index is smaller, it means we can do it earlier here so we should "steal" it
|
||||
qDebug() << "Stealing" << node << "to" << thread;
|
||||
path.RemoveEntry(node);
|
||||
} else {
|
||||
// Otherwise, we'll be delaying it and there's no point to that
|
||||
can_take_node = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Add this node to the thread path
|
||||
if (can_take_node) {
|
||||
qDebug() << node << "will run on thread" << thread << "at index" << index;
|
||||
path.AddEntry(node, thread, index);
|
||||
}
|
||||
|
||||
QList<Node*> deps = node->RunDependencies(nullptr, time);
|
||||
|
||||
// Print debugging information
|
||||
foreach (Node* dep, deps) {
|
||||
qDebug() << " Dependency found:" << dep;
|
||||
}
|
||||
|
||||
int next_index = index + 1;
|
||||
|
||||
foreach (Node* dep, deps) {
|
||||
int ideal_thread = path.FindAvailableThreadAtIndex(index);
|
||||
|
||||
// FIXME: This will throw this thread's index off by one, test whether this is okay
|
||||
int run_thread = (ideal_thread == -1) ? thread : ideal_thread;
|
||||
|
||||
TraverseNode(path, dep, time, run_thread, next_index);
|
||||
}*/
|
||||
}
|
||||
@@ -33,14 +33,17 @@ RendererThread::RendererThread(QOpenGLContext *share_ctx, const int &width, cons
|
||||
{
|
||||
}
|
||||
|
||||
void RendererThread::Queue(const RenderThreadPath &path, const rational& time)
|
||||
bool RendererThread::Queue(const NodeDependency& dep, bool wait)
|
||||
{
|
||||
// Wait for thread to be available
|
||||
mutex_.lock();
|
||||
if (wait) {
|
||||
// Wait for thread to be available
|
||||
mutex_.lock();
|
||||
} else if (!mutex_.tryLock()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We can now change params without the other thread using them
|
||||
path_ = path;
|
||||
time_ = time;
|
||||
path_ = dep;
|
||||
|
||||
// Prepare to wait for thread to respond
|
||||
caller_mutex_.lock();
|
||||
@@ -52,6 +55,8 @@ void RendererThread::Queue(const RenderThreadPath &path, const rational& time)
|
||||
// Wait for thread to start before returning
|
||||
wait_cond_.wait(&caller_mutex_);
|
||||
caller_mutex_.unlock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RendererThread::Cancel()
|
||||
@@ -97,9 +102,20 @@ void RendererThread::run()
|
||||
caller_mutex_.unlock();
|
||||
|
||||
// Process the Node
|
||||
/*for (int i=path_.size()-1;i>=0;i--) {
|
||||
path_.at(i)->Run();
|
||||
}*/
|
||||
NodeOutput* output_to_process = path_.node();
|
||||
Node* node_to_process = output_to_process->parent();
|
||||
|
||||
QList<NodeDependency> deps = node_to_process->RunDependencies(output_to_process, path_.time());
|
||||
|
||||
// Ask for other threads to run these deps while we're here
|
||||
if (!deps.isEmpty()) {
|
||||
for (int i=1;i<deps.size();i++) {
|
||||
emit RequestSibling(deps.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
// Get the requested value
|
||||
output_to_process->get_value(path_.time());
|
||||
|
||||
emit FinishedPath();
|
||||
}
|
||||
@@ -115,8 +131,6 @@ void RendererThread::run()
|
||||
|
||||
void RendererThread::StartThread(QThread::Priority priority)
|
||||
{
|
||||
path_.clear();
|
||||
|
||||
caller_mutex_.lock();
|
||||
|
||||
// Start the thread
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
|
||||
#include "node/node.h"
|
||||
#include "render/renderinstance.h"
|
||||
#include "renderpath.h"
|
||||
|
||||
class RendererThread : public QThread
|
||||
{
|
||||
@@ -40,7 +39,7 @@ public:
|
||||
const olive::PixelFormat& format,
|
||||
const olive::RenderMode& mode);
|
||||
|
||||
void Queue(const RenderThreadPath& path, const rational &time);
|
||||
bool Queue(const NodeDependency &dep, bool wait);
|
||||
|
||||
void Cancel();
|
||||
|
||||
@@ -51,6 +50,8 @@ public:
|
||||
void StartThread(Priority priority = InheritPriority);
|
||||
|
||||
signals:
|
||||
void RequestSibling(NodeDependency dep);
|
||||
|
||||
void FinishedPath();
|
||||
|
||||
private:
|
||||
@@ -62,7 +63,7 @@ private:
|
||||
|
||||
QMutex caller_mutex_;
|
||||
|
||||
RenderThreadPath path_;
|
||||
NodeDependency path_;
|
||||
|
||||
rational time_;
|
||||
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
#include "renderpath.h"
|
||||
|
||||
RenderPath::RenderPath(int max_threads) :
|
||||
finalized_(false)
|
||||
{
|
||||
Q_ASSERT(max_threads > 0);
|
||||
|
||||
render_path_.resize(max_threads);
|
||||
}
|
||||
|
||||
void RenderPath::AddEntry(Node *node, int thread, int index)
|
||||
{
|
||||
if (finalized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
RenderThreadPath& thread_path = render_path_[thread];
|
||||
|
||||
// Make sure there are enough indices for this entry
|
||||
while (thread_path.size() < index) {
|
||||
thread_path.append(nullptr);
|
||||
}
|
||||
|
||||
thread_path.append(node);
|
||||
thread_map_.insert(node, thread);
|
||||
}
|
||||
|
||||
void RenderPath::RemoveEntry(Node *node)
|
||||
{
|
||||
if (finalized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
Q_ASSERT(thread_map_.contains(node));
|
||||
|
||||
int node_thread = thread_map_[node];
|
||||
|
||||
render_path_[node_thread].replace(NodeIndexInThread(node, node_thread), nullptr);
|
||||
thread_map_.remove(node);
|
||||
}
|
||||
|
||||
int RenderPath::ContainsNode(Node *node)
|
||||
{
|
||||
if (thread_map_.contains(node)) {
|
||||
return thread_map_[node];
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int RenderPath::NodeIndex(Node *node)
|
||||
{
|
||||
if (thread_map_.contains(node)) {
|
||||
return NodeIndexInThread(node, thread_map_[node]);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int RenderPath::NodeIndexInThread(Node *node, int thread)
|
||||
{
|
||||
return render_path_[thread].indexOf(node);
|
||||
}
|
||||
|
||||
void RenderPath::Finalize()
|
||||
{
|
||||
for (int i=0;i<render_path_.size();i++) {
|
||||
render_path_[i].removeAll(nullptr);
|
||||
}
|
||||
|
||||
finalized_ = true;
|
||||
}
|
||||
|
||||
int RenderPath::FindAvailableThreadAtIndex(int index)
|
||||
{
|
||||
for (int i=0;i<render_path_.size();i++) {
|
||||
QVector<Node*>& thread_path_ = render_path_[i];
|
||||
|
||||
if (index >= thread_path_.size() || thread_path_.at(index) == nullptr) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
RenderThreadPath &RenderPath::GetThreadPath(int thread)
|
||||
{
|
||||
return render_path_[thread];
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
#ifndef RENDERPATH_H
|
||||
#define RENDERPATH_H
|
||||
|
||||
#include <QVector>
|
||||
|
||||
#include "node/node.h"
|
||||
|
||||
using RenderThreadPath = QVector<Node*>;
|
||||
|
||||
class RenderPath
|
||||
{
|
||||
public:
|
||||
RenderPath(int max_threads);
|
||||
|
||||
/**
|
||||
* @brief Add a Node to this thread at this index
|
||||
*
|
||||
* The Node is guaranteed to be added to at least this index. The index may be higher if this thread has other Nodes
|
||||
* taking up this index, but it will never be lower.
|
||||
*/
|
||||
void AddEntry(Node* node, int thread, int index);
|
||||
|
||||
/**
|
||||
* @brief Removes a Node, replacing its entry with nullptr
|
||||
*
|
||||
* This function asserts whether the Node was added initially to help aid bug detection.
|
||||
*
|
||||
* @param node
|
||||
*/
|
||||
void RemoveEntry(Node* node);
|
||||
|
||||
/**
|
||||
* @brief Determine whether the RenderPath already contains this Node
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* The thread index if one contains this Node, or -1 if none of them do
|
||||
*/
|
||||
int ContainsNode(Node* node);
|
||||
|
||||
/**
|
||||
* @brief Determine what index a Node has in whatever thread it's in
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* The Node's index in its thread, or -1 if no threads have this Node.
|
||||
*/
|
||||
int NodeIndex(Node* node);
|
||||
|
||||
/**
|
||||
* @brief Determine what index a Node has in a particular thread
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* The Node's index in this thread, or -1 if this thread does not have this Node.
|
||||
*/
|
||||
int NodeIndexInThread(Node* node, int thread);
|
||||
|
||||
/**
|
||||
* @brief Finalize the render path ready for executing
|
||||
*
|
||||
* Probing uses indices to determine what Nodes occur where. Depending on the state of the graph, many of these
|
||||
* indices will be empty across all the threads which, while helpful for probing, is unnecessary for executing.
|
||||
* Running this function assumes no further probing will occur.
|
||||
*
|
||||
* Finalizing places this RenderPath into more-or-less a read-only state.
|
||||
*/
|
||||
void Finalize();
|
||||
|
||||
int FindAvailableThreadAtIndex(int index);
|
||||
|
||||
RenderThreadPath& GetThreadPath(int thread);
|
||||
|
||||
private:
|
||||
QVector<RenderThreadPath> render_path_;
|
||||
|
||||
QMap<Node*, int> thread_map_;
|
||||
|
||||
bool finalized_;
|
||||
};
|
||||
|
||||
#endif // RENDERPATH_H
|
||||
@@ -90,3 +90,28 @@ RenderFramebuffer *RenderInstance::buffer()
|
||||
{
|
||||
return &buffer_;
|
||||
}
|
||||
|
||||
QOpenGLContext *RenderInstance::context()
|
||||
{
|
||||
return &ctx_;
|
||||
}
|
||||
|
||||
const int &RenderInstance::width() const
|
||||
{
|
||||
return width_;
|
||||
}
|
||||
|
||||
const int &RenderInstance::height() const
|
||||
{
|
||||
return height_;
|
||||
}
|
||||
|
||||
const olive::PixelFormat &RenderInstance::format() const
|
||||
{
|
||||
return format_;
|
||||
}
|
||||
|
||||
const olive::RenderMode &RenderInstance::mode() const
|
||||
{
|
||||
return mode_;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,16 @@ public:
|
||||
|
||||
RenderFramebuffer* buffer();
|
||||
|
||||
QOpenGLContext* context();
|
||||
|
||||
const int& width() const;
|
||||
|
||||
const int& height() const;
|
||||
|
||||
const olive::PixelFormat& format() const;
|
||||
|
||||
const olive::RenderMode& mode() const;
|
||||
|
||||
private:
|
||||
QOpenGLContext ctx_;
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ void NodeParamViewWidgetBridge::CreateWidgets()
|
||||
footage_combobox->SetRoot(pp->project()->root());
|
||||
|
||||
// Use multiple values
|
||||
footage_combobox->SetFootage(Node::ValueToPtr<Footage>(base_input->get_value()));
|
||||
footage_combobox->SetFootage(Node::ValueToPtr<Footage>(base_input->get_value(0)));
|
||||
|
||||
connect(footage_combobox, SIGNAL(FootageChanged(Footage*)), this, SLOT(WidgetCallback()));
|
||||
// End test code
|
||||
|
||||
Reference in New Issue
Block a user