diff --git a/app/node/blend/alphaover/alphaover.cpp b/app/node/blend/alphaover/alphaover.cpp index 645d8b6a9..e21d09f44 100644 --- a/app/node/blend/alphaover/alphaover.cpp +++ b/app/node/blend/alphaover/alphaover.cpp @@ -50,7 +50,7 @@ bool AlphaOverBlend::IsAccelerated() const return true; } -QString AlphaOverBlend::CodeFragment() const +QString AlphaOverBlend::AcceleratedCodeFragment() const { return ReadFileAsString(":/shaders/alphaover.frag"); } diff --git a/app/node/blend/alphaover/alphaover.h b/app/node/blend/alphaover/alphaover.h index a0dc65747..9667c08ec 100644 --- a/app/node/blend/alphaover/alphaover.h +++ b/app/node/blend/alphaover/alphaover.h @@ -35,7 +35,7 @@ public: virtual QString Description() const override; virtual bool IsAccelerated() const override; - virtual QString CodeFragment() const override; + virtual QString AcceleratedCodeFragment() const override; protected: diff --git a/app/node/block/transition/crossdissolve/crossdissolve.cpp b/app/node/block/transition/crossdissolve/crossdissolve.cpp index ca9b1c25b..ea2ae9ee1 100644 --- a/app/node/block/transition/crossdissolve/crossdissolve.cpp +++ b/app/node/block/transition/crossdissolve/crossdissolve.cpp @@ -29,7 +29,7 @@ bool CrossDissolveTransition::IsAccelerated() const return true; } -QString CrossDissolveTransition::CodeFragment() const +QString CrossDissolveTransition::AcceleratedCodeFragment() const { return ReadFileAsString(":/shaders/crossdissolve.frag"); } diff --git a/app/node/block/transition/crossdissolve/crossdissolve.h b/app/node/block/transition/crossdissolve/crossdissolve.h index 2472ea995..68f4010b1 100644 --- a/app/node/block/transition/crossdissolve/crossdissolve.h +++ b/app/node/block/transition/crossdissolve/crossdissolve.h @@ -15,7 +15,7 @@ public: virtual QString Description() const override; virtual bool IsAccelerated() const override; - virtual QString CodeFragment() const override; + virtual QString AcceleratedCodeFragment() const override; }; #endif // CROSSDISSOLVETRANSITION_H diff --git a/app/node/block/transition/diptoblack/diptoblack.cpp b/app/node/block/transition/diptoblack/diptoblack.cpp index 24f2f90a0..9252a2b58 100644 --- a/app/node/block/transition/diptoblack/diptoblack.cpp +++ b/app/node/block/transition/diptoblack/diptoblack.cpp @@ -29,7 +29,7 @@ bool DipToBlackTransition::IsAccelerated() const return true; } -QString DipToBlackTransition::CodeFragment() const +QString DipToBlackTransition::AcceleratedCodeFragment() const { return ReadFileAsString(":/shaders/diptoblack.frag"); } diff --git a/app/node/block/transition/diptoblack/diptoblack.h b/app/node/block/transition/diptoblack/diptoblack.h index f1a2b2856..99e436cba 100644 --- a/app/node/block/transition/diptoblack/diptoblack.h +++ b/app/node/block/transition/diptoblack/diptoblack.h @@ -15,7 +15,7 @@ public: virtual QString Description() const override; virtual bool IsAccelerated() const override; - virtual QString CodeFragment() const override; + virtual QString AcceleratedCodeFragment() const override; }; #endif // DIPTOBLACKTRANSITION_H diff --git a/app/node/color/opacity/opacity.cpp b/app/node/color/opacity/opacity.cpp index 3d29cded8..55465f999 100644 --- a/app/node/color/opacity/opacity.cpp +++ b/app/node/color/opacity/opacity.cpp @@ -70,7 +70,7 @@ bool OpacityNode::IsAccelerated() const return true; } -QString OpacityNode::CodeFragment() const +QString OpacityNode::AcceleratedCodeFragment() const { return ReadFileAsString(":/shaders/opacity.frag"); } diff --git a/app/node/color/opacity/opacity.h b/app/node/color/opacity/opacity.h index 76f7b79fa..4c83b5da5 100644 --- a/app/node/color/opacity/opacity.h +++ b/app/node/color/opacity/opacity.h @@ -40,7 +40,7 @@ public: virtual void Retranslate() override; virtual bool IsAccelerated() const override; - virtual QString CodeFragment() const override; + virtual QString AcceleratedCodeFragment() const override; NodeInput* texture_input() const; diff --git a/app/node/generator/solid/solid.cpp b/app/node/generator/solid/solid.cpp index 4faab28eb..d19829ac4 100644 --- a/app/node/generator/solid/solid.cpp +++ b/app/node/generator/solid/solid.cpp @@ -58,7 +58,7 @@ bool SolidGenerator::IsAccelerated() const return true; } -QString SolidGenerator::CodeFragment() const +QString SolidGenerator::AcceleratedCodeFragment() const { return ReadFileAsString(":/shaders/solid.frag"); } diff --git a/app/node/generator/solid/solid.h b/app/node/generator/solid/solid.h index 15b077118..d416d83eb 100644 --- a/app/node/generator/solid/solid.h +++ b/app/node/generator/solid/solid.h @@ -42,7 +42,7 @@ public: virtual QString Description() const override; virtual bool IsAccelerated() const override; - virtual QString CodeFragment() const override; + virtual QString AcceleratedCodeFragment() const override; virtual void Retranslate() override; diff --git a/app/node/input/media/video/video.cpp b/app/node/input/media/video/video.cpp index 64a4b7973..b9854f6aa 100644 --- a/app/node/input/media/video/video.cpp +++ b/app/node/input/media/video/video.cpp @@ -51,12 +51,12 @@ bool VideoInput::IsAccelerated() const return true; } -QString VideoInput::CodeVertex() const +QString VideoInput::AcceleratedCodeVertex() const { return ReadFileAsString(":/shaders/videoinput.vert"); } -QString VideoInput::CodeFragment() const +QString VideoInput::AcceleratedCodeFragment() const { return ReadFileAsString(":/shaders/videoinput.frag"); } diff --git a/app/node/input/media/video/video.h b/app/node/input/media/video/video.h index 6727915d2..5040f17f2 100644 --- a/app/node/input/media/video/video.h +++ b/app/node/input/media/video/video.h @@ -21,8 +21,8 @@ public: NodeInput* matrix_input() const; virtual bool IsAccelerated() const override; - virtual QString CodeVertex() const override; - virtual QString CodeFragment() const override; + virtual QString AcceleratedCodeVertex() const override; + virtual QString AcceleratedCodeFragment() const override; virtual void Retranslate() override; diff --git a/app/node/node.cpp b/app/node/node.cpp index 22974b504..10254bda5 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -370,16 +370,26 @@ bool Node::IsAccelerated() const return false; } -QString Node::CodeVertex() const +QString Node::AcceleratedCodeVertex() const { return QString(); } -QString Node::CodeFragment() const +QString Node::AcceleratedCodeFragment() const { return QString(); } +int Node::AcceleratedCodeIterations() const +{ + return 1; +} + +NodeInput *Node::AcceleratedCodeIterativeInput() const +{ + return nullptr; +} + NodeParam *Node::GetParameterWithID(const QString &id) const { foreach (NodeParam* param, params_) { diff --git a/app/node/node.h b/app/node/node.h index 39e896f4c..4f4978bcf 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -137,12 +137,24 @@ public: /** * @brief Generate hardware accelerated code for this Node */ - virtual QString CodeVertex() const; + virtual QString AcceleratedCodeVertex() const; /** * @brief Generate hardware accelerated code for this Node */ - virtual QString CodeFragment() const; + virtual QString AcceleratedCodeFragment() const; + + /** + * @brief Number of iterations to run the accelerated code + * + * Some code is faster if it's merely repeated on a resulting texture rather than run once on the same buffer. + */ + virtual int AcceleratedCodeIterations() const; + + /** + * @brief Parameter that should receive the buffer on an iteration past the first + */ + virtual NodeInput* AcceleratedCodeIterativeInput() const; /** * @brief Returns the parameter with the specified ID (or nullptr if it doesn't exist) diff --git a/app/render/backend/opengl/openglbackend.cpp b/app/render/backend/opengl/openglbackend.cpp index 7f7a83627..f51c4e2d3 100644 --- a/app/render/backend/opengl/openglbackend.cpp +++ b/app/render/backend/opengl/openglbackend.cpp @@ -86,8 +86,8 @@ bool OpenGLBackend::CompileInternal() // Since we have shader code, compile it now OpenGLShaderPtr program; - QString frag_code = n->CodeFragment(); - QString vert_code = n->CodeVertex(); + QString frag_code = n->AcceleratedCodeFragment(); + QString vert_code = n->AcceleratedCodeVertex(); if (frag_code.isEmpty()) { frag_code = OpenGLShader::CodeDefaultFragment(); diff --git a/app/render/backend/opengl/openglworker.cpp b/app/render/backend/opengl/openglworker.cpp index 11513ef00..357177858 100644 --- a/app/render/backend/opengl/openglworker.cpp +++ b/app/render/backend/opengl/openglworker.cpp @@ -154,11 +154,15 @@ void OpenGLWorker::RunNodeAccelerated(const Node *node, const TimeRange &range, return; } - // Create the output texture - OpenGLTextureCache::ReferencePtr output_ref = texture_cache_->Get(ctx_, video_params()); + // Create the output textures + QList dst_refs; + dst_refs.append(texture_cache_->Get(ctx_, video_params())); + GLuint iterative_input = 0; - buffer_.Attach(output_ref->texture(), true); - buffer_.Bind(); + // If this node requires multiple iterations, get a texture for it too + if (node->AcceleratedCodeIterations() > 1 && node->AcceleratedCodeIterativeInput()) { + dst_refs.append(texture_cache_->Get(ctx_, video_params())); + } // Lock the shader so no other thread interferes as we set parameters and draw (and we don't interfere with any others) shader->Lock(); @@ -237,6 +241,11 @@ void OpenGLWorker::RunNodeAccelerated(const Node *node, const TimeRange &range, } } + // If this texture binding is the iterative input, set it here + if (input == node->AcceleratedCodeIterativeInput()) { + iterative_input = input_texture_count; + } + olive::gl::PrepareToDraw(functions_); input_texture_count++; @@ -300,8 +309,32 @@ void OpenGLWorker::RunNodeAccelerated(const Node *node, const TimeRange &range, } } - // Blit this texture through this shader - olive::gl::Blit(shader); + // Some nodes use multiple iterations for optimization + OpenGLTextureCache::ReferencePtr output_tex; + for (int iteration=0;iterationAcceleratedCodeIterations();iteration++) { + // Set iteration number + shader->setUniformValue("ove_iteration", iteration); + + // If this is not the first iteration, set the parameter that will receive the last iteration's texture + OpenGLTextureCache::ReferencePtr source_tex = dst_refs.at((iteration+1)%dst_refs.size()); + OpenGLTextureCache::ReferencePtr destination_tex = dst_refs.at(iteration%dst_refs.size()); + if (iteration > 0) { + functions_->glActiveTexture(GL_TEXTURE0 + iterative_input); + functions_->glBindTexture(GL_TEXTURE_2D, source_tex->texture()->texture()); + } + + buffer_.Attach(destination_tex->texture(), true); + buffer_.Bind(); + + // Blit this texture through this shader + olive::gl::Blit(shader); + + buffer_.Release(); + buffer_.Detach(); + + // Update output reference to the last texture we wrote to + output_tex = destination_tex; + } // Make sure all OpenGL functions are complete by this point before unlocking the shader (or another thread may // change its parameters before our drawing in this thread is done) @@ -319,10 +352,7 @@ void OpenGLWorker::RunNodeAccelerated(const Node *node, const TimeRange &range, shader->release(); - buffer_.Release(); - buffer_.Detach(); - - output_params->Push(NodeParam::kTexture, QVariant::fromValue(output_ref)); + output_params->Push(NodeParam::kTexture, QVariant::fromValue(output_tex)); } void OpenGLWorker::TextureToBuffer(const QVariant &tex_in, QByteArray &buffer)