made vast improvements to rendering engine and node structure

This was many changes that were largely fundamentally related. They included:
- More const modifiers to enforce read only node graphs
- Support for fragment and vertex shaders from the nodes
- Support for node code loaded externally (embedded into the binary)
- Fixed issue preventing two textures from being used in a shader
- Removed several unused functions and cleaned up code
- Fixed video media node misreading its matrix input
This commit is contained in:
itsmattkc
2019-12-09 23:50:52 +11:00
parent daafcbfc95
commit 26b3993b9d
31 changed files with 287 additions and 233 deletions
+2 -1
View File
@@ -34,7 +34,8 @@ public:
virtual QString id() const override;
virtual QString Description() const override;
virtual QString Code() const override;
virtual bool IsAccelerated() const override;
virtual QString CodeFragment() const override;
protected:
+3
View File
@@ -14,6 +14,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
add_subdirectory(crossdissolve)
add_subdirectory(diptoblack)
set(OLIVE_SOURCES
${OLIVE_SOURCES}
node/block/transition/transition.h
+6 -1
View File
@@ -65,7 +65,12 @@ void OpacityNode::Retranslate()
texture_input_->set_name(tr("Texture"));
}
QString OpacityNode::Code() const
bool OpacityNode::IsAccelerated() const
{
return true;
}
QString OpacityNode::CodeFragment() const
{
return "#version 110"
"\n"
+2 -1
View File
@@ -39,7 +39,8 @@ public:
virtual void Retranslate() override;
virtual QString Code() const override;
virtual bool IsAccelerated() const override;
virtual QString CodeFragment() const override;
NodeInput* texture_input() const;
+6 -1
View File
@@ -53,7 +53,12 @@ QString SolidGenerator::Description() const
return tr("Generate a solid color.");
}
QString SolidGenerator::Code() const
bool SolidGenerator::IsAccelerated() const
{
return true;
}
QString SolidGenerator::CodeFragment() const
{
return "#version 110\n"
"\n"
+2 -1
View File
@@ -41,7 +41,8 @@ public:
virtual QString Category() const override;
virtual QString Description() const override;
virtual QString Code() const override;
virtual bool IsAccelerated() const override;
virtual QString CodeFragment() const override;
virtual void Retranslate() override;
-9
View File
@@ -108,12 +108,3 @@ bool NodeGraph::ContainsNode(Node *n)
{
return (n->parent() == this);
}
void NodeGraph::Release()
{
QList<Node*> all_nodes = nodes();
foreach (Node* n, all_nodes) {
n->Release();
}
}
-5
View File
@@ -85,11 +85,6 @@ public:
*/
bool ContainsNode(Node* n);
/**
* @brief Releases all Nodes in this NodeGraph
*/
void Release();
signals:
/**
* @brief Signal emitted when a Node is added to the graph
+13 -16
View File
@@ -1,6 +1,7 @@
#include "video.h"
#include <QDebug>
#include <QMatrix4x4>
#include <QOpenGLPixelTransferOptions>
#include "core.h"
@@ -40,28 +41,24 @@ QString VideoInput::Description() const
return tr("Import a video footage stream.");
}
void VideoInput::Release()
{
MediaInput::Release();
}
NodeInput *VideoInput::matrix_input() const
{
return matrix_input_;
}
QString VideoInput::Code() const
bool VideoInput::IsAccelerated() const
{
return "#version 110\n"
"\n"
"varying vec2 v_texcoord;\n"
"\n"
"uniform sampler2D footage_in;\n"
"uniform mat4 matrix_in;\n"
"\n"
"void main(void) {\n"
" gl_FragColor = texture2D(footage_in, vec2(vec4(v_texcoord, 0.0, 1.0) * matrix_in));\n"
"}\n";
return true;
}
QString VideoInput::CodeVertex() const
{
return ReadFileAsString(":/shaders/videoinput.vert");
}
QString VideoInput::CodeFragment() const
{
return ReadFileAsString(":/shaders/videoinput.frag");
}
void VideoInput::Retranslate()
+3 -3
View File
@@ -18,11 +18,11 @@ public:
virtual QString Category() const override;
virtual QString Description() const override;
virtual void Release() override;
NodeInput* matrix_input() const;
virtual QString Code() const override;
virtual bool IsAccelerated() const override;
virtual QString CodeVertex() const override;
virtual QString CodeFragment() const override;
virtual void Retranslate() override;
+39 -9
View File
@@ -21,6 +21,7 @@
#include "node.h"
#include <QDebug>
#include <QFile>
Node::Node() :
can_be_deleted_(true)
@@ -58,10 +59,6 @@ QString Node::Description() const
return QString();
}
void Node::Release()
{
}
void Node::Retranslate()
{
}
@@ -153,6 +150,18 @@ void Node::DependentEdgeChanged(NodeInput *from)
}
}
QString Node::ReadFileAsString(const QString &filename)
{
QFile f(filename);
QString file_data;
if (f.open(QFile::ReadOnly | QFile::Text)) {
QTextStream text_stream(&f);
file_data = text_stream.readAll();
f.close();
}
return file_data;
}
void Node::LockUserInput()
{
user_input_lock_.lock();
@@ -179,11 +188,9 @@ void Node::CopyInputs(Node *source, Node *destination, bool include_connections)
if (p->type() == NodeParam::kInput) {
NodeInput* src = static_cast<NodeInput*>(p);
if (src->dependent()) {
NodeInput* dst = static_cast<NodeInput*>(dst_param.at(i));
NodeInput* dst = static_cast<NodeInput*>(dst_param.at(i));
NodeInput::CopyValues(src, dst, include_connections, false);
}
NodeInput::CopyValues(src, dst, include_connections, false);
}
}
@@ -359,7 +366,17 @@ QList<Node *> Node::GetImmediateDependencies() const
return node_list;
}
QString Node::Code() const
bool Node::IsAccelerated() const
{
return false;
}
QString Node::CodeVertex() const
{
return QString();
}
QString Node::CodeFragment() const
{
return QString();
}
@@ -442,6 +459,19 @@ NodeOutput *Node::output() const
return output_;
}
QVariant Node::InputValueFromTable(NodeInput *input, const NodeValueTable &table) const
{
NodeParam::DataType find_data_type = input->data_type();
// Exception for Footage types (try to get a Texture instead)
if (find_data_type == NodeParam::kFootage) {
find_data_type = NodeParam::kTexture;
}
// Try to get a value from it
return table.Get(find_data_type);
}
void Node::AddInput(NodeInput *input)
{
AddParameter(input);
+13 -7
View File
@@ -97,11 +97,6 @@ public:
*/
virtual QString Description() const;
/**
* @brief Signals the Node that it won't be used for a while and can deallocate some memory
*/
virtual void Release();
/**
* @brief Function called to retranslate parameter names (should be overridden in derivatives)
*/
@@ -136,10 +131,17 @@ public:
*/
QList<Node*> GetImmediateDependencies() const;
virtual bool IsAccelerated() const;
/**
* @brief Generate OpenCL hardware accelerated code for this Node
* @brief Generate hardware accelerated code for this Node
*/
virtual QString Code() const;
virtual QString CodeVertex() const;
/**
* @brief Generate hardware accelerated code for this Node
*/
virtual QString CodeFragment() const;
/**
* @brief Returns the parameter with the specified ID (or nullptr if it doesn't exist)
@@ -266,6 +268,8 @@ public:
NodeOutput* output() const;
virtual QVariant InputValueFromTable(NodeInput* input, const NodeValueTable& table) const;
protected:
void AddInput(NodeInput* input);
@@ -275,6 +279,8 @@ protected:
virtual void DependentEdgeChanged(NodeInput* from);
static QString ReadFileAsString(const QString& filename);
public slots:
signals:
+23 -8
View File
@@ -102,16 +102,31 @@ TrackOutput* TrackList::AddTrack()
NodeParam::ConnectEdge(track->output(), assoc_input);
// FIXME: Test code only
/*if (track_input_->GetSize() > 1) {
if (current_last_track->output()->IsConnected()) {
AlphaOverBlend* blend = new AlphaOverBlend();
GetParentGraph()->AddNode(blend);
if (track_input_->GetSize() > 1) {
TrackOutput* last_track = nullptr;
NodeParam::ConnectEdge(track->output(), blend->blend_input());
NodeParam::ConnectEdge(current_last_track->output(), blend->base_input());
NodeParam::ConnectEdge(blend->output(), current_last_track->output()->edges().first()->input());
for (int i=track_cache_.size()-1;i>=0;i--) {
TrackOutput* test_track = track_cache_.at(i);
if (test_track && test_track != track) {
last_track = test_track;
break;
}
}
}*/
if (last_track && last_track->output()->IsConnected()) {
foreach (NodeEdgePtr edge, last_track->output()->edges()) {
if (edge->input()->parentNode() != track_input_->parentNode()) {
AlphaOverBlend* blend = new AlphaOverBlend();
GetParentGraph()->AddNode(blend);
NodeParam::ConnectEdge(track->output(), blend->blend_input());
NodeParam::ConnectEdge(last_track->output(), blend->base_input());
NodeParam::ConnectEdge(blend->output(), edge->input());
}
}
}
}
// End test code
return track;
+1
View File
@@ -70,6 +70,7 @@ void Sequence::add_default_nodes()
Node* audio_track_output = timeline_output_->track_list(TrackType::kTrackTypeAudio)->AddTrack();
NodeParam::ConnectEdge(video_track_output->output(), viewer_output_->texture_input());
NodeParam::ConnectEdge(audio_track_output->output(), viewer_output_->samples_input());
//timeline_output_->track_list(TrackType::kTrackTypeVideo)->AddTrack();
// Update the timebase on these nodes
set_video_params(video_params_);
+2 -2
View File
@@ -32,7 +32,7 @@ void AudioRenderBackend::InvalidateCache(const rational &start_range, const rati
RenderBackend::InvalidateCache(start_range, end_range);
rational start_range_adj = qMax(rational(0), start_range);
rational end_range_adj = qMin(SequenceLength(), end_range);
rational end_range_adj = qMin(GetSequenceLength(), end_range);
// Add the range to the list
cache_queue_.append(TimeRange(start_range_adj, end_range_adj));
@@ -41,7 +41,7 @@ void AudioRenderBackend::InvalidateCache(const rational &start_range, const rati
ValidateRanges();
// Queue value update
QueueValueUpdate(TimeRange(start_range, end_range));
QueueValueUpdate();
// Start caching cycle if it hasn't started already
CacheNext();
+3 -3
View File
@@ -28,7 +28,7 @@ FramePtr AudioRenderWorker::RetrieveFromDecoder(DecoderPtr decoder, const TimeRa
return decoder->RetrieveAudio(range.in(), range.out() - range.in(), audio_params_);
}
NodeValueTable AudioRenderWorker::RenderBlock(TrackOutput *track, const TimeRange &range)
NodeValueTable AudioRenderWorker::RenderBlock(const TrackOutput *track, const TimeRange &range)
{
QList<Block*> active_blocks = track->BlocksAtTimeRange(range);
@@ -42,8 +42,8 @@ NodeValueTable AudioRenderWorker::RenderBlock(TrackOutput *track, const TimeRang
TimeRange range_for_block(qMax(b->in(), range.in()),
qMin(b->out(), range.out()));
NodeValueTable table = RenderAsSibling(NodeDependency(b,
range_for_block));
NodeValueTable table = ProcessNode(NodeDependency(b,
range_for_block));
QByteArray samples_from_this_block = table.Take(NodeParam::kSamples).toByteArray();
int destination_offset = audio_params_.time_to_bytes(range_for_block.in() - range.in());
+1 -1
View File
@@ -18,7 +18,7 @@ protected:
virtual FramePtr RetrieveFromDecoder(DecoderPtr decoder, const TimeRange& range) override;
virtual NodeValueTable RenderBlock(TrackOutput *track, const TimeRange& range) override;
virtual NodeValueTable RenderBlock(const TrackOutput *track, const TimeRange& range) override;
private:
AudioRenderingParams audio_params_;
+4 -5
View File
@@ -20,7 +20,6 @@
#include "functions.h"
#include <QOpenGLFunctions>
#include <QOpenGLExtraFunctions>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
@@ -64,7 +63,7 @@ const GLfloat flipped_blit_texcoords[] = {
*
* Currently active QOpenGLFunctions object (use context()->functions() if unsure).
*/
void PrepareToDraw(QOpenGLFunctions* f) {
void olive::gl::PrepareToDraw(QOpenGLFunctions* f) {
f->glGenerateMipmap(GL_TEXTURE_2D);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@@ -96,8 +95,8 @@ void olive::gl::Blit(OpenGLShaderPtr pipeline, bool flipped, QMatrix4x4 matrix)
pipeline->bind();
pipeline->setUniformValue("mvp_matrix", matrix);
pipeline->setUniformValue("texture", 0);
pipeline->setUniformValue("ove_mvpmat", matrix);
pipeline->setUniformValue("ove_maintex", 0);
GLuint vertex_location = static_cast<GLuint>(pipeline->attributeLocation("a_position"));
m_vbo.bind();
@@ -135,7 +134,7 @@ void olive::gl::OCIOBlit(OpenGLShaderPtr pipeline,
pipeline->bind();
pipeline->setUniformValue("tex2", 2);
pipeline->setUniformValue("ove_ociolut", 2);
olive::gl::Blit(pipeline, flipped, matrix);
+3
View File
@@ -22,6 +22,7 @@
#define OPENGLFUNCTIONS_H
#include <QMatrix4x4>
#include <QOpenGLFunctions>
#include "openglshader.h"
@@ -47,6 +48,8 @@ void Blit(OpenGLShaderPtr pipeline, bool flipped = false, QMatrix4x4 matrix = QM
void OCIOBlit(OpenGLShaderPtr pipeline, GLuint lut, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4());
void PrepareToDraw(QOpenGLFunctions* f);
}
}
+14 -8
View File
@@ -46,8 +46,6 @@ bool OpenGLBackend::InitInternal()
void OpenGLBackend::CloseInternal()
{
Decompile();
master_texture_ = nullptr;
}
@@ -79,16 +77,26 @@ bool OpenGLBackend::CompileInternal()
// Check if we have a shader or not
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()) {
if (!n->IsAccelerated()) {
// We enter a null shader so we don't try to compile this again
shader_cache_.Add(n->id(), nullptr);
} else {
// Since we have shader code, compile it now
OpenGLShaderPtr program;
QString frag_code = n->CodeFragment();
QString vert_code = n->CodeVertex();
if (frag_code.isEmpty()) {
frag_code = OpenGLShader::CodeDefaultFragment();
}
if (vert_code.isEmpty()) {
vert_code = OpenGLShader::CodeDefaultVertex();
}
if (!(program = std::make_shared<OpenGLShader>())) {
SetError(QStringLiteral("Failed to create OpenGL shader object"));
return false;
@@ -99,12 +107,12 @@ bool OpenGLBackend::CompileInternal()
return false;
}
if (!program->addShaderFromSourceCode(QOpenGLShader::Fragment, node_code)) {
if (!program->addShaderFromSourceCode(QOpenGLShader::Fragment, frag_code)) {
SetError(QStringLiteral("Failed to add OpenGL fragment shader code"));
return false;
}
if (!program->addShaderFromSourceCode(QOpenGLShader::Vertex, OpenGLShader::CodeDefaultVertex())) {
if (!program->addShaderFromSourceCode(QOpenGLShader::Vertex, vert_code)) {
SetError(QStringLiteral("Failed to add OpenGL vertex shader code"));
return false;
}
@@ -115,8 +123,6 @@ bool OpenGLBackend::CompileInternal()
}
shader_cache_.Add(n->id(), program);
//qDebug() << "Compiled" << connected_output->parent()->id() << "->" << connected_output->id();
}
}
}
+19 -20
View File
@@ -30,7 +30,6 @@ OpenGLShaderPtr OpenGLShader::CreateOCIO(QOpenGLContext* ctx,
OCIO::ConstProcessorRcPtr processor,
bool alpha_is_associated)
{
QOpenGLExtraFunctions* xf = ctx->extraFunctions();
// Create LUT texture
@@ -94,9 +93,9 @@ OpenGLShaderPtr OpenGLShader::CreateOCIO(QOpenGLContext* ctx,
shader_text.append(CodeAlphaReassociate(reassociate_func_name));
// Make OCIO call pass through disassociate and reassociate function
shader_call = QString("%3(%1(%2(col), tex2));").arg(ocio_func_name,
disassociate_func_name,
reassociate_func_name);
shader_call = QStringLiteral("%3(%1(%2(col), ove_ociolut));").arg(ocio_func_name,
disassociate_func_name,
reassociate_func_name);
} else {
@@ -107,18 +106,18 @@ OpenGLShaderPtr OpenGLShader::CreateOCIO(QOpenGLContext* ctx,
shader_text.append(CodeAlphaAssociate(associate_func_name));
// Make OCIO call pass through associate function
shader_call = QString("%2(%1(col, tex2));").arg(ocio_func_name, associate_func_name);
shader_call = QStringLiteral("%2(%1(col, ove_ociolut));").arg(ocio_func_name, associate_func_name);
}
// Add process() function, which GetPipeline() will call if specified
QString process_function_name = "process";
shader_text.append(QString("\n"
"uniform sampler3D tex2;\n"
"\n"
"vec4 %2(vec4 col) {\n"
" return %1\n"
"}\n").arg(shader_call, process_function_name));
shader_text.append(QStringLiteral("\n"
"uniform sampler3D ove_ociolut;\n"
"\n"
"vec4 %2(vec4 col) {\n"
" return %1\n"
"}\n").arg(shader_call, process_function_name));
// Get pipeline-based shader to inject OCIO shader into
@@ -139,10 +138,10 @@ QString OpenGLShader::CodeDefaultFragment(const QString &function_name, const QS
"precision highp float;\n"
"#endif\n"
"\n"
"uniform sampler2D texture;\n"
"uniform sampler2D ove_maintex;\n"
"uniform bool color_only;\n"
"uniform vec4 color_only_color;\n"
"varying vec2 v_texcoord;\n"
"varying vec2 ove_texcoord;\n"
"\n");
// Finish the function with the main function
@@ -157,7 +156,7 @@ QString OpenGLShader::CodeDefaultFragment(const QString &function_name, const QS
" if (color_only) {\n"
" gl_FragColor = color_only_color;"
" } else {\n"
" vec4 color = texture2D(texture, v_texcoord);\n"
" vec4 color = texture2D(ove_maintex, ove_texcoord);\n"
" gl_FragColor = color;\n"
" }\n"
"}\n"));
@@ -167,13 +166,13 @@ QString OpenGLShader::CodeDefaultFragment(const QString &function_name, const QS
// If additional code was passed, add it and reference it in main().
//
// The function in the additional code is expected to be `vec4 function_name(vec4 color)`. The texture coordinate
// can be acquired through `v_texcoord`.
// can be acquired through `ove_texcoord`.
frag_code.append(shader_code);
frag_code.append(QStringLiteral("\n"
"void main() {\n"
" vec4 color = %1(texture2D(texture, v_texcoord));\n"
" vec4 color = %1(texture2D(ove_maintex, ove_texcoord));\n"
" gl_FragColor = color;\n"
"}\n").arg(function_name));
@@ -192,16 +191,16 @@ QString OpenGLShader::CodeDefaultVertex()
"precision highp float;\n"
"#endif\n"
"\n"
"uniform mat4 mvp_matrix;\n"
"uniform mat4 ove_mvpmat;\n"
"\n"
"attribute vec4 a_position;\n"
"attribute vec2 a_texcoord;\n"
"\n"
"varying vec2 v_texcoord;\n"
"varying vec2 ove_texcoord;\n"
"\n"
"void main() {\n"
" gl_Position = mvp_matrix * a_position;\n"
" v_texcoord = a_texcoord;\n"
" gl_Position = ove_mvpmat * a_position;\n"
" ove_texcoord = a_texcoord;\n"
"}\n");
}
+19 -13
View File
@@ -141,7 +141,7 @@ void OpenGLWorker::ParametersChangedEvent()
}
}
void OpenGLWorker::RunNodeAccelerated(Node *node, const NodeValueDatabase *input_params, NodeValueTable *output_params)
void OpenGLWorker::RunNodeAccelerated(const Node *node, const NodeValueDatabase *input_params, NodeValueTable *output_params)
{
OpenGLShaderPtr shader = shader_cache_->Get(node->id());
@@ -174,15 +174,7 @@ void OpenGLWorker::RunNodeAccelerated(Node *node, const NodeValueDatabase *input
// Get value from database at this input
const NodeValueTable& input_data = (*input_params)[input];
NodeParam::DataType find_data_type = input->data_type();
// Exception for Footage types (try to get a Texture instead)
if (find_data_type == NodeParam::kFootage) {
find_data_type = NodeParam::kTexture;
}
// Try to get a value from it
QVariant value = input_data.Get(find_data_type);
QVariant value = node->InputValueFromTable(input, input_data);
switch (input->data_type()) {
case NodeInput::kInt:
@@ -225,6 +217,16 @@ void OpenGLWorker::RunNodeAccelerated(Node *node, const NodeValueDatabase *input
// Set value to bound texture
shader->setUniformValue(variable_location, input_texture_count);
// Set texture resolution if shader wants it
int res_param_location = shader->uniformLocation(QStringLiteral("%1_resolution").arg(input->id()));
if (res_param_location > -1) {
shader->setUniformValue(res_param_location,
static_cast<GLfloat>(texture->width()),
static_cast<GLfloat>(texture->height()));
}
olive::gl::PrepareToDraw(functions_);
input_texture_count++;
break;
}
@@ -247,9 +249,14 @@ void OpenGLWorker::RunNodeAccelerated(Node *node, const NodeValueDatabase *input
}
}
// Ensure viewport is correct
// Set up OpenGL parameters as necessary
functions_->glViewport(0, 0, video_params().effective_width(), video_params().effective_height());
// Provide some standard args
shader->setUniformValue("ove_resolution",
static_cast<GLfloat>(video_params().width()),
static_cast<GLfloat>(video_params().height()));
// Blit this texture through this shader
olive::gl::Blit(shader);
@@ -305,9 +312,8 @@ void OpenGLWorker::FinishInit()
// Store OpenGL functions instance
functions_ = ctx_->functions();
functions_->glBlendFunc(GL_ONE, GL_ZERO);
// Set up OpenGL parameters as necessary
functions_->glEnable(GL_BLEND);
ParametersChangedEvent();
buffer_.Create(ctx_);
+1 -1
View File
@@ -46,7 +46,7 @@ protected:
virtual void FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable* table) override;
virtual void RunNodeAccelerated(Node *node, const NodeValueDatabase *input_params, NodeValueTable* output_params) override;
virtual void RunNodeAccelerated(const Node *node, const NodeValueDatabase *input_params, NodeValueTable* output_params) override;
virtual void TextureToBuffer(const QVariant& texture, QByteArray& buffer) override;
+46 -28
View File
@@ -9,9 +9,9 @@ RenderBackend::RenderBackend(QObject *parent) :
started_(false),
viewer_node_(nullptr),
copied_viewer_node_(nullptr),
value_update_queued_(false),
recompile_queued_(false),
input_update_queued_(false)
input_update_queued_(false),
blocking_(false)
{
}
@@ -51,6 +51,8 @@ void RenderBackend::Close()
started_ = false;
Decompile();
CloseInternal();
for (int i=0;i<processors_.size();i++) {
@@ -127,12 +129,14 @@ bool RenderBackend::Compile()
foreach (Node* n, source_node_list_) {
Node* copy = n->copy();
// Copy values (but not connections yet)
Node::CopyInputs(n, copy, false);
copied_graph_.AddNode(copy);
}
// We just copied the inputs, so if an input update is queued, it's unnecessary
input_update_queued_ = false;
// We know that the first node will be the viewer node since we appended that first in the copy
copied_viewer_node_ = static_cast<ViewerOutput*>(copied_graph_.nodes().first());
@@ -183,7 +187,7 @@ void RenderBackend::RegenerateCacheID()
CacheIDChangedEvent(cache_id_);
}
rational RenderBackend::SequenceLength()
rational RenderBackend::GetSequenceLength()
{
if (viewer_node_ == nullptr) {
return 0;
@@ -216,16 +220,24 @@ void RenderBackend::CacheNext()
}
if ((input_update_queued_ || recompile_queued_) && !AllProcessorsAreAvailable()) {
qDebug() << "Blocking render while we wait for all workers to finish...";
if (input_update_queued_ && recompile_queued_) {
qDebug() << "Input update and recompile queued, blocking while processors are busy...";
} else if (input_update_queued_) {
qDebug() << "Input update queued, blocking while processors are busy...";
} else {
qDebug() << "Recompile queued, blocking while processors are busy...";
}
blocking_ = true;
return;
}
if (input_update_queued_) {
UpdateNodeInputs();
input_update_queued_ = false;
if (blocking_) {
qDebug() << "Processors are free now";
blocking_ = false;
}
if (recompile_queued_) {
qDebug() << "Recompile requested, decompiling now...";
Decompile();
recompile_queued_ = false;
}
@@ -235,6 +247,25 @@ void RenderBackend::CacheNext()
return;
}
if (input_update_queued_) {
qDebug() << "Copied inputs from UI graph";
for (int i=0;i<source_node_list_.size();i++) {
Node* src = source_node_list_.at(i);
Node* dst = copied_graph_.nodes().at(i);
Node::CopyInputs(src, dst, false);
}
input_update_queued_ = false;
}
Node* node_connected_to_viewer = GetDependentInput()->get_connected_node();
if (!node_connected_to_viewer) {
return;
}
foreach (RenderWorker* worker, processors_) {
if (cache_queue_.isEmpty()) {
break;
@@ -243,14 +274,16 @@ void RenderBackend::CacheNext()
if (!WorkerIsBusy(worker)) {
TimeRange cache_frame = cache_queue_.takeFirst();
NodeDependency dep = NodeDependency(GetDependentInput()->get_connected_node(), cache_frame.in(), cache_frame.out());
//qDebug() << "Rendering" << cache_frame.in().toDouble() << "-" << cache_frame.out().toDouble();
NodeDependency dep = NodeDependency(node_connected_to_viewer, cache_frame.in(), cache_frame.out());
SetWorkerBusyState(worker, true);
QMetaObject::invokeMethod(worker,
"Render",
Qt::QueuedConnection,
Q_ARG(NodeDependency, dep));
SetWorkerBusyState(worker, true);
}
}
}
@@ -270,24 +303,9 @@ const QString &RenderBackend::cache_id() const
return cache_id_;
}
void RenderBackend::QueueValueUpdate(const TimeRange &range)
void RenderBackend::QueueValueUpdate()
{
value_update_queued_ = true;
value_update_range_ = range;
}
void RenderBackend::UpdateNodeInputs()
{
if (value_update_queued_) {
for (int i=0;i<source_node_list_.size();i++) {
Node* src = source_node_list_.at(i);
Node* dst = copied_graph_.nodes().at(i);
Node::CopyInputs(src, dst, false);
}
value_update_queued_ = false;
}
input_update_queued_ = true;
}
bool RenderBackend::WorkerIsBusy(RenderWorker *worker) const
+4 -7
View File
@@ -47,7 +47,7 @@ protected:
virtual void DecompileInternal() = 0;
rational SequenceLength();
rational GetSequenceLength();
const QVector<QThread*>& threads();
@@ -82,9 +82,7 @@ protected:
const QString& cache_id() const;
void QueueValueUpdate(const TimeRange& range);
void UpdateNodeInputs();
void QueueValueUpdate();
bool WorkerIsBusy(RenderWorker* worker) const;
void SetWorkerBusyState(RenderWorker* worker, bool busy);
@@ -130,14 +128,13 @@ private:
QList<Node*> source_node_list_;
NodeGraph copied_graph_;
bool value_update_queued_;
TimeRange value_update_range_;
bool recompile_queued_;
bool input_update_queued_;
QVector<bool> processor_busy_state_;
bool blocking_;
private slots:
void QueueRecompile();
+11 -28
View File
@@ -37,31 +37,9 @@ void RenderWorker::Render(NodeDependency path)
emit CompletedCache(path, RenderInternal(path));
}
NodeValueTable RenderWorker::RenderAsSibling(NodeDependency dep)
{
Node* node = dep.node();
QList<NodeInput*> connected_inputs;
NodeValueTable value;
//qDebug() << "Processing" << node->id();
// 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()) {
// If the range is not wholly contained in this Block, we'll need to do some extra processing
value = RenderBlock(static_cast<TrackOutput*>(node), dep.range());
} else {
value = ProcessNodeNormally(NodeDependency(node, dep.range()));
}
// We're done!
return value;
}
NodeValueTable RenderWorker::RenderInternal(const NodeDependency &path)
{
return RenderAsSibling(path);
return ProcessNode(path);
}
bool RenderWorker::OutputIsAccelerated(Node *output)
@@ -70,7 +48,7 @@ bool RenderWorker::OutputIsAccelerated(Node *output)
return false;
}
void RenderWorker::RunNodeAccelerated(Node *node, const NodeValueDatabase *input_params, NodeValueTable* output_params)
void RenderWorker::RunNodeAccelerated(const Node *node, const NodeValueDatabase *input_params, NodeValueTable* output_params)
{
Q_UNUSED(node)
Q_UNUSED(input_params)
@@ -102,9 +80,14 @@ bool RenderWorker::IsStarted()
return started_;
}
NodeValueTable RenderWorker::ProcessNodeNormally(const NodeDependency& dep)
NodeValueTable RenderWorker::ProcessNode(const NodeDependency& dep)
{
Node* node = dep.node();
const Node* node = dep.node();
if (node->IsTrack()) {
// If the range is not wholly contained in this Block, we'll need to do some extra processing
return RenderBlock(static_cast<const TrackOutput*>(node), dep.range());
}
// FIXME: Cache certain values here if we've already processed them before
@@ -119,8 +102,8 @@ NodeValueTable RenderWorker::ProcessNodeNormally(const NodeDependency& dep)
if (input->IsConnected()) {
// Value will equal something from the connected node, follow it
table = ProcessNodeNormally(NodeDependency(input->get_connected_node(),
input_time));
table = ProcessNode(NodeDependency(input->get_connected_node(),
input_time));
} else {
// Push onto the table the value at this time from the input
QVariant input_value = input->get_value_at_time(input_time.in());
+3 -5
View File
@@ -25,8 +25,6 @@ public slots:
void Render(NodeDependency path);
NodeValueTable RenderAsSibling(NodeDependency dep);
signals:
void CompletedCache(NodeDependency dep, NodeValueTable data);
@@ -39,7 +37,7 @@ protected:
virtual bool OutputIsAccelerated(Node *output);
virtual void RunNodeAccelerated(Node *node, const NodeValueDatabase *input_params, NodeValueTable* output_params);
virtual void RunNodeAccelerated(const Node *node, const NodeValueDatabase *input_params, NodeValueTable* output_params);
StreamPtr ResolveStreamFromInput(NodeInput* input);
DecoderPtr ResolveDecoderFromInput(StreamPtr stream);
@@ -48,9 +46,9 @@ protected:
virtual void FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable* table) = 0;
NodeValueTable ProcessNodeNormally(const NodeDependency &dep);
NodeValueTable ProcessNode(const NodeDependency &dep);
virtual NodeValueTable RenderBlock(TrackOutput *track, const TimeRange& range) = 0;
virtual NodeValueTable RenderBlock(const TrackOutput *track, const TimeRange& range) = 0;
private:
bool started_;
+3 -3
View File
@@ -47,7 +47,7 @@ void VideoRenderBackend::InvalidateCache(const rational &start_range, const rati
// Adjust range to min/max values
rational start_range_adj = qMax(rational(0), start_range);
rational end_range_adj = qMin(SequenceLength(), end_range);
rational end_range_adj = qMin(GetSequenceLength(), end_range);
qDebug() << "Cache invalidated between"
<< start_range_adj.toDouble()
@@ -99,10 +99,10 @@ void VideoRenderBackend::InvalidateCache(const rational &start_range, const rati
}
// Remove frames after this time code if it's changed
frame_cache_.Truncate(SequenceLength());
frame_cache_.Truncate(GetSequenceLength());
// Queue value update
QueueValueUpdate(TimeRange(start_range, end_range));
QueueValueUpdate();
CacheNext();
}
+39 -41
View File
@@ -31,7 +31,7 @@ NodeValueTable VideoRenderWorker::RenderInternal(const NodeDependency& path)
emit HashAlreadyExists(path, hash);
} else if (frame_cache_->TryCache(hash)) {
// This hash is available for us to cache, start traversing graph
value = RenderAsSibling(path);
value = ProcessNode(path);
emit CompletedFrame(path, hash, value);
} else {
@@ -47,11 +47,11 @@ FramePtr VideoRenderWorker::RetrieveFromDecoder(DecoderPtr decoder, const TimeRa
return decoder->RetrieveVideo(range.in());
}
void VideoRenderWorker::HashNodeRecursively(QCryptographicHash *hash, Node* n, const rational& time)
void VideoRenderWorker::HashNodeRecursively(QCryptographicHash *hash, const Node* n, const rational& time)
{
// Resolve BlockList
if (n->IsTrack()) {
n = static_cast<TrackOutput*>(n)->BlockAtTime(time);
n = static_cast<const TrackOutput*>(n)->BlockAtTime(time);
if (!n) {
return;
@@ -66,52 +66,50 @@ void VideoRenderWorker::HashNodeRecursively(QCryptographicHash *hash, Node* n, c
if (param->type() == NodeParam::kInput) {
NodeInput* input = static_cast<NodeInput*>(param);
if (input->dependent()) {
// Get time adjustment
TimeRange range = n->InputTimeAdjustment(input, TimeRange(time, time));
// Get time adjustment
TimeRange range = n->InputTimeAdjustment(input, TimeRange(time, time));
// For a single frame, we only care about one of the times
rational input_time = range.in();
// For a single frame, we only care about one of the times
rational input_time = range.in();
if (input->IsConnected()) {
// Traverse down this edge
HashNodeRecursively(hash, input->get_connected_node(), input_time);
} else {
// Grab the value at this time
QVariant value = input->get_value_at_time(input_time);
hash->addData(NodeParam::ValueToBytes(input->data_type(), value));
}
if (input->IsConnected()) {
// Traverse down this edge
HashNodeRecursively(hash, input->get_connected_node(), input_time);
} else {
// Grab the value at this time
QVariant value = input->get_value_at_time(input_time);
hash->addData(NodeParam::ValueToBytes(input->data_type(), value));
}
// 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(stream);
// 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(stream);
if (decoder != nullptr) {
// Add footage details to hash
if (decoder != nullptr) {
// Add footage details to hash
// Footage filename
hash->addData(stream->footage()->filename().toUtf8());
// Footage filename
hash->addData(stream->footage()->filename().toUtf8());
// Footage last modified date
hash->addData(stream->footage()->timestamp().toString().toUtf8());
// Footage last modified date
hash->addData(stream->footage()->timestamp().toString().toUtf8());
// Footage stream
hash->addData(QString::number(stream->index()).toUtf8());
// Footage stream
hash->addData(QString::number(stream->index()).toUtf8());
if (stream->type() == Stream::kImage || stream->type() == Stream::kVideo) {
ImageStreamPtr video_stream = std::static_pointer_cast<ImageStream>(stream);
if (stream->type() == Stream::kImage || stream->type() == Stream::kVideo) {
ImageStreamPtr video_stream = std::static_pointer_cast<ImageStream>(stream);
// Footage timestamp
hash->addData(QString::number(decoder->GetTimestampFromTime(time)).toUtf8());
// Footage timestamp
hash->addData(QString::number(decoder->GetTimestampFromTime(time)).toUtf8());
// Current colorspace
// FIXME: Handle empty colorspace...
hash->addData(video_stream->colorspace().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());
}
// Alpha associated setting
hash->addData(QString::number(video_stream->premultiplied_alpha()).toUtf8());
}
}
}
@@ -162,7 +160,7 @@ void VideoRenderWorker::Download(NodeDependency dep, QByteArray hash, QVariant t
}
}
NodeValueTable VideoRenderWorker::RenderBlock(TrackOutput *track, const TimeRange &range)
NodeValueTable VideoRenderWorker::RenderBlock(const TrackOutput *track, const TimeRange &range)
{
// A frame can only have one active block so we just validate the in point of the range
Block* active_block = track->BlockAtTime(range.in());
@@ -170,8 +168,8 @@ NodeValueTable VideoRenderWorker::RenderBlock(TrackOutput *track, const TimeRang
NodeValueTable table;
if (active_block) {
table = RenderAsSibling(NodeDependency(active_block,
range));
table = ProcessNode(NodeDependency(active_block,
range));
}
return table;
+2 -4
View File
@@ -43,14 +43,12 @@ protected:
virtual FramePtr RetrieveFromDecoder(DecoderPtr decoder, const TimeRange& range) override;
virtual NodeValueTable RenderBlock(TrackOutput *track, const TimeRange& range) override;
virtual NodeValueTable RenderBlock(const TrackOutput *track, const TimeRange& range) override;
ColorProcessorCache* color_cache();
private:
void ProcessNode();
void HashNodeRecursively(QCryptographicHash* hash, Node *n, const rational &time);
void HashNodeRecursively(QCryptographicHash* hash, const Node *n, const rational &time);
VideoRenderingParams video_params_;
-2
View File
@@ -49,8 +49,6 @@ void NodeView::SetGraph(NodeGraph *graph)
disconnect(graph_, SIGNAL(NodeRemoved(Node*)), this, SLOT(RemoveNode(Node*)));
disconnect(graph_, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SLOT(AddEdge(NodeEdgePtr)));
disconnect(graph_, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(RemoveEdge(NodeEdgePtr)));
graph_->Release();
}
// Clear the scene of all UI objects