diff --git a/app/node/blend/alphaover/alphaover.h b/app/node/blend/alphaover/alphaover.h index 7289260c5..a0dc65747 100644 --- a/app/node/blend/alphaover/alphaover.h +++ b/app/node/blend/alphaover/alphaover.h @@ -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: diff --git a/app/node/block/transition/CMakeLists.txt b/app/node/block/transition/CMakeLists.txt index 65f672588..60ebb509e 100644 --- a/app/node/block/transition/CMakeLists.txt +++ b/app/node/block/transition/CMakeLists.txt @@ -14,6 +14,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +add_subdirectory(crossdissolve) +add_subdirectory(diptoblack) + set(OLIVE_SOURCES ${OLIVE_SOURCES} node/block/transition/transition.h diff --git a/app/node/color/opacity/opacity.cpp b/app/node/color/opacity/opacity.cpp index da579b53a..f3df5c33e 100644 --- a/app/node/color/opacity/opacity.cpp +++ b/app/node/color/opacity/opacity.cpp @@ -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" diff --git a/app/node/color/opacity/opacity.h b/app/node/color/opacity/opacity.h index 2eae10ff4..76f7b79fa 100644 --- a/app/node/color/opacity/opacity.h +++ b/app/node/color/opacity/opacity.h @@ -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; diff --git a/app/node/generator/solid/solid.cpp b/app/node/generator/solid/solid.cpp index 63c2d4b63..2de06eeab 100644 --- a/app/node/generator/solid/solid.cpp +++ b/app/node/generator/solid/solid.cpp @@ -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" diff --git a/app/node/generator/solid/solid.h b/app/node/generator/solid/solid.h index 1c8cc7106..15b077118 100644 --- a/app/node/generator/solid/solid.h +++ b/app/node/generator/solid/solid.h @@ -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; diff --git a/app/node/graph.cpp b/app/node/graph.cpp index c2901ec0a..e6f1187ac 100644 --- a/app/node/graph.cpp +++ b/app/node/graph.cpp @@ -108,12 +108,3 @@ bool NodeGraph::ContainsNode(Node *n) { return (n->parent() == this); } - -void NodeGraph::Release() -{ - QList all_nodes = nodes(); - - foreach (Node* n, all_nodes) { - n->Release(); - } -} diff --git a/app/node/graph.h b/app/node/graph.h index d7a159b94..df3613648 100644 --- a/app/node/graph.h +++ b/app/node/graph.h @@ -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 diff --git a/app/node/input/media/video/video.cpp b/app/node/input/media/video/video.cpp index cbd91dfa4..64a4b7973 100644 --- a/app/node/input/media/video/video.cpp +++ b/app/node/input/media/video/video.cpp @@ -1,6 +1,7 @@ #include "video.h" #include +#include #include #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() diff --git a/app/node/input/media/video/video.h b/app/node/input/media/video/video.h index 1f666ae09..6727915d2 100644 --- a/app/node/input/media/video/video.h +++ b/app/node/input/media/video/video.h @@ -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; diff --git a/app/node/node.cpp b/app/node/node.cpp index 4f35c6f52..f9b10f784 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -21,6 +21,7 @@ #include "node.h" #include +#include 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(p); - if (src->dependent()) { - NodeInput* dst = static_cast(dst_param.at(i)); + NodeInput* dst = static_cast(dst_param.at(i)); - NodeInput::CopyValues(src, dst, include_connections, false); - } + NodeInput::CopyValues(src, dst, include_connections, false); } } @@ -359,7 +366,17 @@ QList 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); diff --git a/app/node/node.h b/app/node/node.h index 3cd1f752f..8a6646130 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -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 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: diff --git a/app/node/output/timeline/tracklist.cpp b/app/node/output/timeline/tracklist.cpp index 839b9f2d1..79c7968f2 100644 --- a/app/node/output/timeline/tracklist.cpp +++ b/app/node/output/timeline/tracklist.cpp @@ -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; diff --git a/app/project/item/sequence/sequence.cpp b/app/project/item/sequence/sequence.cpp index d9f2c8d71..a7872d3a4 100644 --- a/app/project/item/sequence/sequence.cpp +++ b/app/project/item/sequence/sequence.cpp @@ -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_); diff --git a/app/render/backend/audiorenderbackend.cpp b/app/render/backend/audiorenderbackend.cpp index ba0b0dd07..f2f5d6f8f 100644 --- a/app/render/backend/audiorenderbackend.cpp +++ b/app/render/backend/audiorenderbackend.cpp @@ -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(); diff --git a/app/render/backend/audiorenderworker.cpp b/app/render/backend/audiorenderworker.cpp index 438b62fd7..f1fa9a3b4 100644 --- a/app/render/backend/audiorenderworker.cpp +++ b/app/render/backend/audiorenderworker.cpp @@ -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 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()); diff --git a/app/render/backend/audiorenderworker.h b/app/render/backend/audiorenderworker.h index 4a2fae507..d6f7aaa01 100644 --- a/app/render/backend/audiorenderworker.h +++ b/app/render/backend/audiorenderworker.h @@ -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_; diff --git a/app/render/backend/opengl/functions.cpp b/app/render/backend/opengl/functions.cpp index bd9fcee7a..aa31660c9 100644 --- a/app/render/backend/opengl/functions.cpp +++ b/app/render/backend/opengl/functions.cpp @@ -20,7 +20,6 @@ #include "functions.h" -#include #include #include #include @@ -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(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); diff --git a/app/render/backend/opengl/functions.h b/app/render/backend/opengl/functions.h index f64afe376..779dfce78 100644 --- a/app/render/backend/opengl/functions.h +++ b/app/render/backend/opengl/functions.h @@ -22,6 +22,7 @@ #define OPENGLFUNCTIONS_H #include +#include #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); + } } diff --git a/app/render/backend/opengl/openglbackend.cpp b/app/render/backend/opengl/openglbackend.cpp index 120c610f5..90491a9fa 100644 --- a/app/render/backend/opengl/openglbackend.cpp +++ b/app/render/backend/opengl/openglbackend.cpp @@ -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())) { 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(); } } } diff --git a/app/render/backend/opengl/openglshader.cpp b/app/render/backend/opengl/openglshader.cpp index 97177b411..13d2d8fd1 100644 --- a/app/render/backend/opengl/openglshader.cpp +++ b/app/render/backend/opengl/openglshader.cpp @@ -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"); } diff --git a/app/render/backend/opengl/openglworker.cpp b/app/render/backend/opengl/openglworker.cpp index 82ff47595..560b76f95 100644 --- a/app/render/backend/opengl/openglworker.cpp +++ b/app/render/backend/opengl/openglworker.cpp @@ -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(texture->width()), + static_cast(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(video_params().width()), + static_cast(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_); diff --git a/app/render/backend/opengl/openglworker.h b/app/render/backend/opengl/openglworker.h index f0bba8c76..86bfd936c 100644 --- a/app/render/backend/opengl/openglworker.h +++ b/app/render/backend/opengl/openglworker.h @@ -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; diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp index 63e6e4687..d0761f899 100644 --- a/app/render/backend/renderbackend.cpp +++ b/app/render/backend/renderbackend.cpp @@ -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;icopy(); - // 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(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;iget_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& 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 source_node_list_; NodeGraph copied_graph_; - bool value_update_queued_; - TimeRange value_update_range_; - bool recompile_queued_; bool input_update_queued_; QVector processor_busy_state_; + bool blocking_; + private slots: void QueueRecompile(); diff --git a/app/render/backend/renderworker.cpp b/app/render/backend/renderworker.cpp index 49d02b8cd..5620fd154 100644 --- a/app/render/backend/renderworker.cpp +++ b/app/render/backend/renderworker.cpp @@ -37,31 +37,9 @@ void RenderWorker::Render(NodeDependency path) emit CompletedCache(path, RenderInternal(path)); } -NodeValueTable RenderWorker::RenderAsSibling(NodeDependency dep) -{ - Node* node = dep.node(); - QList 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(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(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()); diff --git a/app/render/backend/renderworker.h b/app/render/backend/renderworker.h index 21530c069..e1bcbdba8 100644 --- a/app/render/backend/renderworker.h +++ b/app/render/backend/renderworker.h @@ -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_; diff --git a/app/render/backend/videorenderbackend.cpp b/app/render/backend/videorenderbackend.cpp index 3e54f4752..5e492894b 100644 --- a/app/render/backend/videorenderbackend.cpp +++ b/app/render/backend/videorenderbackend.cpp @@ -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(); } diff --git a/app/render/backend/videorenderworker.cpp b/app/render/backend/videorenderworker.cpp index b5a33c2d1..28729aae4 100644 --- a/app/render/backend/videorenderworker.cpp +++ b/app/render/backend/videorenderworker.cpp @@ -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(n)->BlockAtTime(time); + n = static_cast(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(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(stream); + if (stream->type() == Stream::kImage || stream->type() == Stream::kVideo) { + ImageStreamPtr video_stream = std::static_pointer_cast(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; diff --git a/app/render/backend/videorenderworker.h b/app/render/backend/videorenderworker.h index 20f5d9b0f..bf84f4ced 100644 --- a/app/render/backend/videorenderworker.h +++ b/app/render/backend/videorenderworker.h @@ -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_; diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index f4aad1240..6bdc32f63 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -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