diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 60b0cceb9..3961058ad 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -96,6 +96,7 @@ target_link_libraries(${OLIVE_TARGET} FFMPEG::swresample ${OPENCOLORIO_LIBRARIES} ${OIIO_LIBRARIES} + OpenCL ) set(OLIVE_EFFECTS diff --git a/app/decoder/frame.h b/app/decoder/frame.h index 8561908ec..8fc44682f 100644 --- a/app/decoder/frame.h +++ b/app/decoder/frame.h @@ -25,7 +25,7 @@ #include #include "common/rational.h" -#include "render/audio/audioparams.h" +#include "render/audioparams.h" #include "render/pixelformat.h" class Frame; diff --git a/app/decoder/wave.h b/app/decoder/wave.h index 73566f3f3..3f0299846 100644 --- a/app/decoder/wave.h +++ b/app/decoder/wave.h @@ -5,7 +5,7 @@ #include #include "audio/sampleformat.h" -#include "render/audio/audioparams.h" +#include "render/audioparams.h" class WaveOutput { diff --git a/app/node/CMakeLists.txt b/app/node/CMakeLists.txt index 64c0ca0aa..dcaa35d5a 100644 --- a/app/node/CMakeLists.txt +++ b/app/node/CMakeLists.txt @@ -24,8 +24,6 @@ add_subdirectory(output) set(OLIVE_SOURCES ${OLIVE_SOURCES} - node/code.h - node/code.cpp node/dependency.h node/dependency.cpp node/edge.h diff --git a/app/node/blend/alphaover/alphaover.cpp b/app/node/blend/alphaover/alphaover.cpp index 2a53dc36a..a111386a0 100644 --- a/app/node/blend/alphaover/alphaover.cpp +++ b/app/node/blend/alphaover/alphaover.cpp @@ -20,11 +20,6 @@ #include "alphaover.h" -#include "render/gl/functions.h" -#include "render/gl/shadergenerators.h" -#include "render/rendertexture.h" -#include "render/video/videorenderer.h" - AlphaOverBlend::AlphaOverBlend() { @@ -45,71 +40,20 @@ QString AlphaOverBlend::Description() return tr("A blending node that composites one texture over another using its alpha channel."); } -NodeCode AlphaOverBlend::Code(NodeOutput *output) +QString AlphaOverBlend::Code(NodeOutput *output) { if (output == texture_output()) { - return NodeCode("AlphaOver", - "void AlphaOver(const pixel *base_in, const pixel *blend_in, pixel *tex_out) {" - " int i = get_global_id(0);" - " tex_out[i].r = base_in.r - blend_in.a + blend_in.r;" - " tex_out[i].g = base_in.g - blend_in.a + blend_in.g;" - " tex_out[i].b = base_in.b - blend_in.a + blend_in.b;" - " tex_out[i].a = base_in.a - blend_in.a + blend_in.a;" - "}"); + return "#version 110" + "\n" + "varying vec2 olive_tex_coord;\n" + "\n" + "uniform sampler2D base_in;\n" + "uniform sampler2D blend_in;\n" + "\n" + "void main(void) {\n" + " gl_FragColor = base_in - blend_in.a + blend_in;\n" + "}\n"; } return Node::Code(output); } - -void AlphaOverBlend::Release() -{ -} - -QVariant AlphaOverBlend::Value(NodeOutput *param, const rational &in, const rational &out) -{ - // Find the current Renderer instance - RenderInstance* renderer = VideoRendererProcessor::CurrentInstance(); - - // If nothing is available, don't return a texture - if (renderer == nullptr) { - return 0; - } - - // The only parameter should be texture output, but for future proofing we put this here - if (param == texture_output()) { - RenderTexturePtr base = base_input()->get_value(in, out).value(); - RenderTexturePtr blend = blend_input()->get_value(in, out).value(); - - if (base == nullptr && blend == nullptr) { - return 0; - } else if (base == nullptr) { - return QVariant::fromValue(blend); - } else if (blend == nullptr) { - return QVariant::fromValue(base); - } - - // Attach framebuffer to the backbuffer of base - renderer->buffer()->Attach(base); - renderer->buffer()->Bind(); - - // Bind blend - blend->Bind(); - - // Set compositing strategy to alpha over - renderer->context()->functions()->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - - // Draw blend on base - olive::gl::Blit(renderer->default_pipeline()); - - // Release all - blend->Release(); - renderer->buffer()->Release(); - renderer->buffer()->Detach(); - - // Return base texture which now has blend composited on top - // NOTE: Blend texture will be implicitly deleted here (if it's not used anywhere else) - return QVariant::fromValue(base); - } - - return 0; -} diff --git a/app/node/blend/alphaover/alphaover.h b/app/node/blend/alphaover/alphaover.h index 83fe15ffd..da9b64788 100644 --- a/app/node/blend/alphaover/alphaover.h +++ b/app/node/blend/alphaover/alphaover.h @@ -22,7 +22,6 @@ #define ALPHAOVER_H #include "node/blend/blend.h" -#include "render/gl/shaderptr.h" class AlphaOverBlend : public BlendNode { @@ -33,12 +32,9 @@ public: virtual QString id() override; virtual QString Description() override; - virtual NodeCode Code(NodeOutput* output) override; - - virtual void Release() override; + virtual QString Code(NodeOutput* output) override; protected: - virtual QVariant Value(NodeOutput* param, const rational &in, const rational &out) override; private: }; diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp index 259a061f3..9586a9e33 100644 --- a/app/node/block/block.cpp +++ b/app/node/block/block.cpp @@ -25,12 +25,12 @@ Block::Block() : next_(nullptr) { - previous_input_ = new NodeInput("prev_block"); + previous_input_ = new NodeInput("prev_in"); previous_input_->set_data_type(NodeParam::kBlock); previous_input_->set_dependent(false); AddParameter(previous_input_); - block_output_ = new NodeOutput("block_out"); + block_output_ = new NodeOutput("this_out"); AddParameter(block_output_); buffer_output_ = new NodeOutput("buffer_out"); @@ -277,3 +277,8 @@ bool Block::HasLinks() return !linked_clips_.isEmpty(); } +bool Block::IsBlock() +{ + return true; +} + diff --git a/app/node/block/block.h b/app/node/block/block.h index 2308cca2f..9facd3593 100644 --- a/app/node/block/block.h +++ b/app/node/block/block.h @@ -78,6 +78,8 @@ public: const QVector& linked_clips(); bool HasLinks(); + virtual bool IsBlock() override; + public slots: /** * @brief Refreshes internal cache of in/out points up to date diff --git a/app/node/code.cpp b/app/node/code.cpp deleted file mode 100644 index 6e3110e73..000000000 --- a/app/node/code.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "nodecode.h" - -NodeCode::NodeCode() -{ - -} - -NodeCode::NodeCode(const QString &function_name, const QString &code) : - function_name_(function_name), - code_(code) -{ -} - -bool NodeCode::IsValid() -{ - return (!function_name_.isEmpty() && !code_.isEmpty()); -} - -const QString &NodeCode::function_name() -{ - return function_name_; -} - -const QString &NodeCode::code() -{ - return code_; -} diff --git a/app/node/code.h b/app/node/code.h deleted file mode 100644 index dcbefc466..000000000 --- a/app/node/code.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef NODECODE_H -#define NODECODE_H - -#include - -class NodeCode -{ -public: - NodeCode(); - NodeCode(const QString& function_name, const QString& code); - - bool IsValid(); - - const QString& function_name(); - const QString& code(); - -private: - QString function_name_; - - QString code_; -}; - -#endif // NODECODE_H diff --git a/app/node/color/opacity/opacity.cpp b/app/node/color/opacity/opacity.cpp index de9a2c8d6..5fce51451 100644 --- a/app/node/color/opacity/opacity.cpp +++ b/app/node/color/opacity/opacity.cpp @@ -20,10 +20,6 @@ #include "opacity.h" -#include "render/gl/functions.h" -#include "render/rendertexture.h" -#include "render/video/videorenderer.h" - OpacityNode::OpacityNode() { opacity_input_ = new NodeInput("opacity_in"); @@ -61,65 +57,29 @@ QString OpacityNode::id() return "org.olivevideoeditor.Olive.opacity"; } -QVariant OpacityNode::Value(NodeOutput *output, const rational &in, const rational &out) -{ - Q_UNUSED(out) - - // Find the current Renderer instance - RenderInstance* renderer = VideoRendererProcessor::CurrentInstance(); - - // If nothing is available, don't return a texture - if (renderer == nullptr) { - return 0; - } - - if (output == texture_output_) { - RenderTexturePtr input_tex = texture_input_->get_value(in).value(); - - if (input_tex == nullptr) { - return 0; - } - - // Attach texture's back buffer as frame buffer - renderer->buffer()->AttachBackBuffer(input_tex); - renderer->buffer()->Bind(); - - // Bind texture's front buffer to draw with - input_tex->Bind(); - - // Set opacity to value - ShaderPtr pipeline = renderer->default_pipeline(); - pipeline->bind(); - pipeline->setUniformValue("opacity", opacity_input_->get_value(in).toFloat()*0.01f); - pipeline->release(); - - renderer->context()->functions()->glBlendFunc(GL_ONE, GL_ZERO); - - // Blit - olive::gl::Blit(pipeline); - - // Reset to full opacity - pipeline->bind(); - pipeline->setUniformValue("opacity", 1.0f); - pipeline->release(); - - input_tex->Release(); - renderer->buffer()->Release(); - renderer->buffer()->Detach(); - - input_tex->SwapFrontAndBack(); - - return QVariant::fromValue(input_tex); - } - - return 0; -} - void OpacityNode::Retranslate() { opacity_input_->set_name(tr("Opacity")); } +QString OpacityNode::Code(NodeOutput *output) +{ + if (output == texture_output()) { + return "#version 110" + "\n" + "varying vec2 olive_tex_coord;\n" + "\n" + "uniform sampler2D tex_in;\n" + "uniform float opacity_in;\n" + "\n" + "void main(void) {\n" + " gl_FragColor = tex_in * (opacity_in * 0.01);\n" + "}\n"; + } + + return Node::Code(output); +} + NodeInput *OpacityNode::texture_input() { return texture_input_; diff --git a/app/node/color/opacity/opacity.h b/app/node/color/opacity/opacity.h index 4f4134033..1b20fe694 100644 --- a/app/node/color/opacity/opacity.h +++ b/app/node/color/opacity/opacity.h @@ -22,7 +22,6 @@ #define OPACITYNODE_H #include "node/node.h" -#include "render/gl/shaderptr.h" class OpacityNode : public Node { @@ -36,10 +35,10 @@ public: virtual QString id() override; - virtual QVariant Value(NodeOutput *output, const rational &in, const rational &out) override; - virtual void Retranslate() override; + virtual QString Code(NodeOutput* output) override; + NodeInput* texture_input(); NodeOutput* texture_output(); diff --git a/app/node/input/media/video/video.cpp b/app/node/input/media/video/video.cpp index f222c4fd4..2afdfb4bd 100644 --- a/app/node/input/media/video/video.cpp +++ b/app/node/input/media/video/video.cpp @@ -6,10 +6,7 @@ #include "core.h" #include "decoder/ffmpeg/ffmpegdecoder.h" #include "project/item/footage/footage.h" -#include "render/gl/shadergenerators.h" -#include "render/gl/functions.h" #include "render/pixelservice.h" -#include "render/video/videorenderer.h" VideoInput::VideoInput() : color_processor_(nullptr), @@ -68,6 +65,25 @@ NodeOutput *VideoInput::texture_output() return texture_output_; } +QString VideoInput::Code(NodeOutput *output) +{ + if (output == texture_output()) { + return "#version 110\n" + "\n" + "varying vec2 olive_tex_coord;\n" + "\n" + "uniform sampler2D footage_in;\n" + "uniform mat4 matrix_in;\n" + "\n" + "void main(void) {\n" + " gl_FragColor = texture2D(olive_tex, vec2(vec4(olive_tex_coord, 0.0, 1.0) * matrix_in));\n" + "}\n"; + } + + return Node::Code(output); +} + +/* void VideoInput::Hash(QCryptographicHash *hash, NodeOutput *from, const rational &time) { Node::Hash(hash, from, time); @@ -253,3 +269,4 @@ QVariant VideoInput::Value(NodeOutput *output, const rational &in, const rationa return 0; } +*/ diff --git a/app/node/input/media/video/video.h b/app/node/input/media/video/video.h index 4c6a84ada..02da4f324 100644 --- a/app/node/input/media/video/video.h +++ b/app/node/input/media/video/video.h @@ -5,8 +5,6 @@ #include "../media.h" #include "render/colormanager.h" -#include "render/rendertexture.h" -#include "render/gl/shadergenerators.h" class VideoInput : public MediaInput { @@ -18,33 +16,24 @@ public: virtual QString Category() override; virtual QString Description() override; - virtual QString Code() override; - virtual void Release() override; NodeInput* matrix_input(); NodeOutput* texture_output(); - virtual void Hash(QCryptographicHash *hash, NodeOutput* from, const rational &time) override; + virtual QString Code(NodeOutput* output) override; + + //virtual void Hash(QCryptographicHash *hash, NodeOutput* from, const rational &time) override; protected: - virtual QVariant Value(NodeOutput* output, const rational& in, const rational& out) override; + //virtual QVariant Value(NodeOutput* output, const rational& in, const rational& out) override; private: NodeInput* matrix_input_; NodeOutput* texture_output_; - RenderTexture internal_tex_; - - ColorProcessorPtr color_processor_; - - ShaderPtr pipeline_; - - QOpenGLContext* ocio_ctx_; - GLuint ocio_texture_; - }; #endif // VIDEOINPUT_H diff --git a/app/node/node.cpp b/app/node/node.cpp index cbb6d9b6d..81e121026 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -72,6 +72,15 @@ void Node::RemoveParameter(NodeParam *param) delete param; } +QVariant Node::Value(NodeOutput *output, const rational &in, const rational &out) +{ + Q_UNUSED(output) + Q_UNUSED(in) + Q_UNUSED(out) + + return QVariant(); +} + void Node::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from) { Q_UNUSED(from) @@ -141,6 +150,11 @@ void Node::SetCanBeDeleted(bool s) can_be_deleted_ = s; } +bool Node::IsBlock() +{ + return false; +} + rational Node::LastProcessedTime() { rational t; @@ -278,11 +292,11 @@ QList Node::GetImmediateDependencies() return node_list; } -NodeCode Node::Code(NodeOutput *output) +QString Node::Code(NodeOutput *output) { Q_UNUSED(output) - return NodeCode(); + return QString(); } QList Node::RunDependencies(NodeOutput *output, const rational &time) diff --git a/app/node/node.h b/app/node/node.h index 8aa35a5c5..6aed68c46 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -26,7 +26,6 @@ #include #include "common/rational.h" -#include "node/code.h" #include "node/dependency.h" #include "node/input.h" #include "node/output.h" @@ -128,7 +127,7 @@ public: /** * @brief Generate OpenCL hardware accelerated code for this Node */ - virtual NodeCode Code(NodeOutput* output); + virtual QString Code(NodeOutput* output); /** * @brief Wrapper for Process() @@ -224,10 +223,18 @@ public: bool CanBeDeleted(); /** - * @brief Set whether + * @brief Set whether this Node can be deleted in the UI or not */ void SetCanBeDeleted(bool s); + /** + * @brief Returns whether this Node is a "Block" type or not + * + * You shouldn't ever need to override this since all derivatives of Block will automatically have this set to true. + * It's just a more convenient way of checking than dynamic_casting. + */ + virtual bool IsBlock(); + protected: /** * @brief Add a parameter to this node @@ -258,7 +265,7 @@ protected: * corresponding output if it's connected to one. If your node doesn't directly deal with time, the default behavior * of the NodeParam objects will handle everything related to it automatically. */ - virtual QVariant Value(NodeOutput* output, const rational &in, const rational &out) = 0; + virtual QVariant Value(NodeOutput* output, const rational &in, const rational &out); /** * @brief Retrieve the last timecode Process() was called with diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index 0cfe15496..ce35f3ecb 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -196,6 +196,16 @@ QVariant TrackOutput::Value(NodeOutput *output, const rational &in, const ration if (output == track_output_) { // Set track output correctly return PtrToValue(this); + } else if (output == buffer_output()) { + ValidateCurrentBlock(in); + + if (current_block_ != this) { + // At this point, we must have found the correct block so we use its texture output to produce the image + return current_block_->buffer_output()->get_value(in, out); + } + + // No texture is valid + return 0; } // Run default node processing diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index b83a448e2..57def7aa2 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -70,16 +70,6 @@ NodeInput *ViewerOutput::length_input() return length_input_; } -RenderTexturePtr ViewerOutput::GetTexture(const rational &time) -{ - return texture_input_->get_value(time).value(); -} - -QByteArray ViewerOutput::GetSamples(const rational &in, const rational &out) -{ - return samples_input_->get_value(in, out).toByteArray(); -} - void ViewerOutput::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from) { Node::InvalidateCache(start_range, end_range, from); diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index 8b38c3f2a..52aacbc3f 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -23,8 +23,7 @@ #include "node/node.h" #include "render/videoparams.h" -#include "render/audio/audioparams.h" -#include "render/rendertexture.h" +#include "render/audioparams.h" /** * @brief A bridge between a node system and a ViewerPanel @@ -46,9 +45,6 @@ public: NodeInput* samples_input(); NodeInput* length_input(); - RenderTexturePtr GetTexture(const rational& time); - QByteArray GetSamples(const rational& in, const rational& out); - virtual void InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from = nullptr) override; const VideoParams& video_params(); diff --git a/app/node/param.h b/app/node/param.h index 0e4f3f26b..40a832442 100644 --- a/app/node/param.h +++ b/app/node/param.h @@ -87,9 +87,6 @@ public: /// Resolves to `Block*` kBlock, - /// Resolves to `QList` - kBlockList, - /// Resolves to `Footage*` kFootage, diff --git a/app/project/item/sequence/sequence.cpp b/app/project/item/sequence/sequence.cpp index 337ffb0f6..fe1e81e2d 100644 --- a/app/project/item/sequence/sequence.cpp +++ b/app/project/item/sequence/sequence.cpp @@ -40,7 +40,7 @@ Sequence::Sequence() : void Sequence::Open(SequencePtr sequence) { - // FIXME: This is fairly "hardcoded" behavior + // FIXME: This is fairly "hardcoded" behavior and doesn't support infinite panels ViewerPanel* viewer_panel = olive::panel_manager->MostRecentlyFocused(); TimelinePanel* timeline_panel = olive::panel_manager->MostRecentlyFocused(); diff --git a/app/project/item/sequence/sequence.h b/app/project/item/sequence/sequence.h index a94ebedae..4c6e8c264 100644 --- a/app/project/item/sequence/sequence.h +++ b/app/project/item/sequence/sequence.h @@ -26,7 +26,6 @@ #include "node/output/timeline/timeline.h" #include "node/output/viewer/viewer.h" #include "render/videoparams.h" -#include "render/video/videorenderer.h" #include "project/item/item.h" class Sequence; diff --git a/app/render/CMakeLists.txt b/app/render/CMakeLists.txt index 2cf3cbfb3..9b4593c6f 100644 --- a/app/render/CMakeLists.txt +++ b/app/render/CMakeLists.txt @@ -14,13 +14,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -add_subdirectory(audio) add_subdirectory(backend) -add_subdirectory(gl) -add_subdirectory(video) set(OLIVE_SOURCES ${OLIVE_SOURCES} + render/audioparams.h + render/audioparams.cpp render/colormanager.h render/colormanager.cpp render/colorprocessor.h @@ -29,13 +28,7 @@ set(OLIVE_SOURCES render/pixelformat.cpp render/pixelservice.h render/pixelservice.cpp - render/renderinstance.h - render/renderinstance.cpp render/rendermodes.h - render/renderframebuffer.h - render/renderframebuffer.cpp - render/rendertexture.h - render/rendertexture.cpp render/videoparams.h render/videoparams.cpp PARENT_SCOPE diff --git a/app/render/audio/CMakeLists.txt b/app/render/audio/CMakeLists.txt deleted file mode 100644 index ba57a7a4d..000000000 --- a/app/render/audio/CMakeLists.txt +++ /dev/null @@ -1,30 +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 . - -set(OLIVE_SOURCES - ${OLIVE_SOURCES} - render/audio/audioparams.h - render/audio/audioparams.cpp - render/audio/audiorenderer.h - render/audio/audiorenderer.cpp - render/audio/audiorendererdownloadthread.h - render/audio/audiorendererdownloadthread.cpp - render/audio/audiorendererprocessthread.h - render/audio/audiorendererprocessthread.cpp - render/audio/audiorendererthreadbase.h - render/audio/audiorendererthreadbase.cpp - PARENT_SCOPE -) diff --git a/app/render/audio/audiorenderer.cpp b/app/render/audio/audiorenderer.cpp deleted file mode 100644 index 8e4d8b201..000000000 --- a/app/render/audio/audiorenderer.cpp +++ /dev/null @@ -1,308 +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 . - -***/ - -#include "audiorenderer.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "common/filefunctions.h" -#include "render/gl/functions.h" -#include "render/gl/shadergenerators.h" -#include "render/pixelservice.h" - -AudioRendererProcessor::AudioRendererProcessor(QObject *parent) : - QObject(parent), - started_(false), - caching_(false), - starting_(false), - viewer_node_(nullptr) -{ - // FIXME: Cache name should actually be the name of the sequence - SetCacheName("Test"); -} - -AudioRendererProcessor::~AudioRendererProcessor() -{ - Stop(); -} - -void AudioRendererProcessor::SetCacheName(const QString &s) -{ - cache_name_ = s; - cache_time_ = QDateTime::currentMSecsSinceEpoch(); - - GenerateCacheIDInternal(); -} - -void AudioRendererProcessor::InvalidateCache(const rational &start_range, const rational &end_range) -{ - // Adjust range to min/max values - rational start_range_adj = qMax(rational(0), start_range); - rational end_range_adj = qMin(viewer_node_->Length(), end_range); - - qDebug() << "Cache invalidated between" - << start_range_adj.toDouble() - << "and" - << end_range_adj.toDouble(); - - bool append = true; - - for (int i=0;i= const_range.in() - && start_range_adj <= const_range.out()) { - append = false; - if (const_range.out() < end_range_adj) { - // Same in point but longer, extend - cache_queue_[i].set_out(end_range_adj); - } - break; - } else if (end_range_adj <= const_range.out() - && end_range_adj >= const_range.in()) { - append = false; - if (const_range.in() > start_range_adj) { - // Same out point but longer, extend - cache_queue_[i].set_in(start_range_adj); - } - break; - } - } - - if (append) { - cache_queue_.append(TimeRange(start_range_adj, end_range_adj)); - } - - CacheNext(); -} - -void AudioRendererProcessor::SetParameters(const AudioRenderingParams& params) -{ - // Since we're changing parameters, all the existing threads are invalid and must be removed. They will start again - // next time this Node has to process anything. - Stop(); - - // Set new parameters - params_ = params; - - // Regenerate the cache ID - GenerateCacheIDInternal(); -} - -void AudioRendererProcessor::Start() -{ - if (started_) { - return; - } - - QOpenGLContext* ctx = QOpenGLContext::currentContext(); - - int background_thread_count = QThread::idealThreadCount(); - - // Some OpenGL implementations (notably wgl) require the context not to be current before sharing - QSurface* old_surface = ctx->surface(); - ctx->doneCurrent(); - - threads_.resize(background_thread_count); - - for (int i=0;i(this, params_); - threads_[i]->StartThread(QThread::LowPriority); - - // Ensure this connection is "Queued" so that it always runs in this object's threaded rather than any of the - // other threads - connect(threads_.at(i).get(), - SIGNAL(RequestSibling(NodeDependency)), - this, - SLOT(ThreadRequestSibling(NodeDependency)), - Qt::QueuedConnection); - } - - // Connect first thread (master thread) to the callback - connect(threads_.first().get(), - SIGNAL(CachedFrame(const QByteArray&, const rational&, const rational&)), - this, - SLOT(ThreadCallback(const QByteArray&, const rational&, const rational&)), - Qt::QueuedConnection); - - // Restore context now that thread creation is complete - ctx->makeCurrent(old_surface); - - started_ = true; -} - -void AudioRendererProcessor::Stop() -{ - if (!started_) { - return; - } - - started_ = false; - - foreach (AudioRendererProcessThreadPtr process_thread, threads_) { - process_thread->Cancel(); - } - threads_.clear(); -} - -void AudioRendererProcessor::GenerateCacheIDInternal() -{ - if (cache_name_.isEmpty() || !params_.is_valid()) { - return; - } - - // Generate an ID that is more or less guaranteed to be unique to this Sequence - QCryptographicHash hash(QCryptographicHash::Sha1); - hash.addData(cache_name_.toUtf8()); - hash.addData(QString::number(cache_time_).toUtf8()); - hash.addData(QString::number(params_.sample_rate()).toUtf8()); - hash.addData(QString::number(params_.channel_layout()).toUtf8()); - hash.addData(QString::number(params_.format()).toUtf8()); - - QByteArray bytes = hash.result(); - cache_id_ = bytes.toHex(); -} - -void AudioRendererProcessor::CacheNext() -{ - if (cache_queue_.isEmpty() || viewer_node_ == nullptr || caching_) { - return; - } - - // Make sure cache has started - Start(); - - TimeRange cache_frame = cache_queue_.takeFirst(); - - qDebug() << "Caching" << cache_frame.in().toDouble() << "-" << cache_frame.out().toDouble(); - - threads_.first()->Queue(NodeDependency(viewer_node_->texture_input()->get_connected_output(), cache_frame), true, false); - - caching_ = true; -} - -QString AudioRendererProcessor::CachePathName(const QByteArray &hash) -{ - QDir this_cache_dir = QDir(GetMediaCacheLocation()).filePath(cache_id_); - this_cache_dir.mkpath("."); - - QString filename = QString("%1.pcm").arg(QString(hash.toHex())); - - return this_cache_dir.filePath(filename); -} - -void AudioRendererProcessor::ThreadCallback(const QByteArray& samples, const rational& in, const rational& out) -{ - // Threads are all done now, time to proceed - caching_ = false; - - int start_offset = params_.time_to_bytes(in); - int end_offset = params_.time_to_bytes(out); - - // Ensure sample cache is at least large enough for this - if (sample_cache_.size() < end_offset) { - sample_cache_.resize(end_offset); - } - - sample_cache_.replace(start_offset, samples.size(), samples); - - CacheNext(); -} - -void AudioRendererProcessor::ThreadRequestSibling(NodeDependency dep) -{ - // Try to queue another thread to run this dep in advance - for (int i=1;iQueue(dep, false, true)) { - return; - } - } -} - -AudioRendererThreadBase* AudioRendererProcessor::CurrentThread() -{ - return dynamic_cast(QThread::currentThread()); -} - -AudioParams *AudioRendererProcessor::CurrentInstance() -{ - AudioRendererThreadBase* thread = CurrentThread(); - - if (thread != nullptr) { - return thread->params(); - } - - return nullptr; -} - -QByteArray AudioRendererProcessor::GetCachedSamples(const rational &in, const rational &out) -{ - if (viewer_node_ == nullptr || in == out) { - // Nothing is connected - nothing to show or render - return nullptr; - } - - if (!params_.is_valid()) { - qWarning() << "Invalid parameters"; - return nullptr; - } - - if (cache_id_.isEmpty()) { - qWarning() << "No cache ID"; - return nullptr; - } - - if (out < in || in < 0 || out < 0) { - qWarning() << "Invalid time requested"; - return nullptr; - } - - int start_offset = qMin(params_.time_to_bytes(in), sample_cache_.size()); - int end_offset = qMin(params_.time_to_bytes(out), sample_cache_.size()); - int length = end_offset - start_offset; - - if (length == 0) { - return nullptr; - } - - return sample_cache_.mid(start_offset, length); -} - -void AudioRendererProcessor::SetViewerNode(ViewerOutput *viewer) -{ - if (viewer_node_ != nullptr) { - disconnect(viewer_node_, SIGNAL(TextureChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&))); - } - - viewer_node_ = viewer; - - if (viewer_node_ != nullptr) { - connect(viewer_node_, SIGNAL(TextureChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&))); - - // FIXME: Hardcoded format and mode - AudioRenderingParams(viewer_node_->audio_params(), olive::SAMPLE_FMT_FLT); - } -} diff --git a/app/render/audio/audiorenderer.h b/app/render/audio/audiorenderer.h deleted file mode 100644 index e19ca4c49..000000000 --- a/app/render/audio/audiorenderer.h +++ /dev/null @@ -1,149 +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 . - -***/ - -#ifndef AUDIORENDERER_H -#define AUDIORENDERER_H - -#include -#include - -#include "common/timerange.h" -#include "node/output/viewer/viewer.h" -#include "render/pixelformat.h" -#include "render/rendermodes.h" -#include "audiorendererdownloadthread.h" -#include "audiorendererprocessthread.h" - -/** - * @brief A multithreaded OpenGL based renderer for node systems - */ -class AudioRendererProcessor : public QObject -{ - Q_OBJECT -public: - /** - * @brief Renderer Constructor - * - * Constructing a Renderer object will not start any threads/backend on its own. Use Start() to do this and Stop() - * when the Renderer is about to be destroyed. - */ - AudioRendererProcessor(QObject* parent); - - virtual ~AudioRendererProcessor() override; - - void SetCacheName(const QString& s); - - /** - * @brief Set parameters of the Renderer - * - * The Renderer owns the buffers that are used in the rendering process and this function sets the kind of buffers - * to use. The Renderer must be stopped when calling this function. - * - * @param width - * - * Buffer width - * - * @param height - * - * Buffer height - * - * @param format - * - * Buffer pixel format - */ - void SetParameters(const AudioRenderingParams ¶ms); - - /** - * @brief Return current instance of a RenderThread (or nullptr if there is none) - * - * This function attempts a dynamic_cast on QThread::currentThread() to RendererThread, which will return nullptr if - * the cast fails (e.g. if this function is called from the main thread rather than a RendererThread). - */ - static AudioRendererThreadBase* CurrentThread(); - - static AudioParams* CurrentInstance(); - - QByteArray GetCachedSamples(const rational& in, const rational& out); - - void SetViewerNode(ViewerOutput* viewer); - -private: - /** - * @brief Allocate and start the multithreaded backend - */ - void Start(); - - /** - * @brief Terminate and deallocate the multithreaded backend - */ - void Stop(); - - /** - * @brief Internal function for generating the cache ID - */ - void GenerateCacheIDInternal(); - - /** - * @brief Function called when there are frames in the queue to cache - * - * This function is NOT thread-safe and should only be called in the main thread. - */ - void CacheNext(); - - /** - * @brief Return the path of the cached image at this time - */ - QString CachePathName(const QByteArray &hash); - - /** - * @brief Internal list of RenderProcessThreads - */ - QVector threads_; - - /** - * @brief Internal variable that contains whether the Renderer has started or not - */ - bool started_; - - AudioRenderingParams params_; - - QList cache_queue_; - QString cache_name_; - qint64 cache_time_; - QString cache_id_; - - bool caching_; - - bool starting_; - - ViewerOutput* viewer_node_; - - QByteArray sample_cache_; - -private slots: - void InvalidateCache(const rational &start_range, const rational &end_range); - - void ThreadCallback(const QByteArray& samples, const rational& in, const rational &out); - - void ThreadRequestSibling(NodeDependency dep); - -}; - -#endif // AUDIORENDERER_H diff --git a/app/render/audio/audiorendererdownloadthread.cpp b/app/render/audio/audiorendererdownloadthread.cpp deleted file mode 100644 index d6bf0538f..000000000 --- a/app/render/audio/audiorendererdownloadthread.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include "audiorendererdownloadthread.h" - -#include -#include -#include - -#include "common/define.h" -#include "render/pixelservice.h" - -/*AudioRendererDownloadThread::AudioRendererDownloadThread(QOpenGLContext *share_ctx, - const int &width, - const int &height, - const int ÷r, - const olive::PixelFormat &format, - const olive::RenderMode &mode) : - AudioRendererThreadBase(share_ctx, width, height, divider, format, mode), - cancelled_(false) -{ -} - -void AudioRendererDownloadThread::Queue(RenderTexturePtr texture, const QString& fn, const QByteArray &hash) -{ - texture_queue_lock_.lock(); - - texture_queue_.append({texture, fn, hash}); - - wait_cond_.wakeAll(); - - texture_queue_lock_.unlock(); -} - -void AudioRendererDownloadThread::Cancel() -{ - cancelled_ = true; - - texture_queue_lock_.lock(); - wait_cond_.wakeAll(); - texture_queue_lock_.unlock(); - - wait(); -} - -void AudioRendererDownloadThread::ProcessLoop() -{ - QOpenGLFunctions* f = render_instance()->context()->functions(); - QOpenGLExtraFunctions* xf = render_instance()->context()->extraFunctions(); - - f->glGenFramebuffers(1, &read_buffer_); - - DownloadQueueEntry entry; - - int buffer_size = PixelService::GetBufferSize(render_instance()->format(), - render_instance()->width(), - render_instance()->height()); - - QVector data_buffer; - data_buffer.resize(buffer_size); - - PixelFormatInfo format_info = PixelService::GetPixelFormatInfo(render_instance()->format()); - - // Set up OIIO::ImageSpec for compressing cached images on disk - OIIO::ImageSpec spec(render_instance()->width(), render_instance()->height(), kRGBAChannels, format_info.oiio_desc); - spec.attribute("compression", "dwaa:200"); - - while (!cancelled_) { - // Check queue for textures to download (use mutex to prevent collisions) - texture_queue_lock_.lock(); - - while (texture_queue_.isEmpty()) { - // Main waiting condition - wait_cond_.wait(&texture_queue_lock_); - - if (cancelled_) { - break; - } - } - if (cancelled_) { - texture_queue_lock_.unlock(); - break; - } - - entry = texture_queue_.takeFirst(); - - texture_queue_lock_.unlock(); - - // Download the texture - - f->glBindFramebuffer(GL_READ_FRAMEBUFFER, read_buffer_); - - xf->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - entry.texture->texture(), - 0); - - f->glReadPixels(0, - 0, - entry.texture->width(), - entry.texture->height(), - format_info.pixel_format, - format_info.pixel_type, - data_buffer.data()); - - xf->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - 0, - 0); - - f->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - - std::string working_fn_std = entry.filename.toStdString(); - - std::unique_ptr out = OIIO::ImageOutput::create(working_fn_std); - - if (out) { - out->open(working_fn_std, spec); - out->write_image(format_info.oiio_desc, data_buffer.data()); - out->close(); - - emit Downloaded(entry.hash); - } else { - qWarning() << tr("Failed to open output file \"%1\"").arg(entry.filename); - } - } - - f->glDeleteFramebuffers(1, &read_buffer_); -}*/ diff --git a/app/render/audio/audiorendererdownloadthread.h b/app/render/audio/audiorendererdownloadthread.h deleted file mode 100644 index f77a3eb99..000000000 --- a/app/render/audio/audiorendererdownloadthread.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef AUDIORENDERERDOWNLOADTHREAD_H -#define AUDIORENDERERDOWNLOADTHREAD_H - -#include "audiorendererthreadbase.h" - -/*class AudioRendererDownloadThread : public AudioRendererThreadBase -{ - Q_OBJECT -public: - AudioRendererDownloadThread(QOpenGLContext* share_ctx, - const int& width, - const int& height, - const int ÷r, - const olive::PixelFormat& format, - const olive::RenderMode& mode); - - void Queue(RenderTexturePtr texture, const QString &fn, const QByteArray &hash); - -public slots: - virtual void Cancel() override; - -signals: - void Downloaded(const QByteArray& hash); - -protected: - virtual void ProcessLoop() override; - -private: - struct DownloadQueueEntry { - RenderTexturePtr texture; - QString filename; - QByteArray hash; - }; - - GLuint read_buffer_; - - QVector texture_queue_; - - QMutex texture_queue_lock_; - - QAtomicInt cancelled_; - - QByteArray hash_; - -}; - -using AudioRendererDownloadThreadPtr = std::shared_ptr;*/ - -#endif // AUDIORENDERERDOWNLOADTHREAD_H diff --git a/app/render/audio/audiorendererprocessthread.cpp b/app/render/audio/audiorendererprocessthread.cpp deleted file mode 100644 index 53ace48af..000000000 --- a/app/render/audio/audiorendererprocessthread.cpp +++ /dev/null @@ -1,116 +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 . - -***/ - -#include "audiorendererprocessthread.h" - -#include "audiorenderer.h" - -AudioRendererProcessThread::AudioRendererProcessThread(AudioRendererProcessor* parent, - const AudioRenderingParams ¶ms) : - AudioRendererThreadBase(params), - parent_(parent), - cancelled_(false) -{ - -} - -bool AudioRendererProcessThread::Queue(const NodeDependency& dep, bool wait, bool sibling) -{ - 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_ = dep; - sibling_ = sibling; - - // Prepare to wait for thread to respond - caller_mutex_.lock(); - - // Wake up our main thread - wait_cond_.wakeAll(); - mutex_.unlock(); - - // Wait for thread to start before returning - wait_cond_.wait(&caller_mutex_); - caller_mutex_.unlock(); - - return true; -} - -void AudioRendererProcessThread::Cancel() -{ - cancelled_ = true; - - mutex_.lock(); - wait_cond_.wakeAll(); - mutex_.unlock(); - - wait(); -} - -void AudioRendererProcessThread::ProcessLoop() -{ - while (!cancelled_) { - // Main waiting condition - wait_cond_.wait(&mutex_); - - if (cancelled_) { - break; - } - - // Wake up main thread - caller_mutex_.lock(); - wait_cond_.wakeAll(); - caller_mutex_.unlock(); - - // Process the Node - NodeOutput* output_to_process = path_.node(); - Node* node_to_process = output_to_process->parent(); - - QList all_deps; - - QList deps = node_to_process->RunDependencies(output_to_process, path_.in()); - - // Ask for other threads to run these deps while we're here - if (!deps.isEmpty()) { - for (int i=1;iget_value(path_.in(), path_.out()).toByteArray(); - - if (!sibling_) { - foreach (Node* dep, all_deps) { - dep->Unlock(); - } - - node_to_process->Unlock(); - } - - // Signal that we cached some samples - emit CachedSamples(samples, path_.in(), path_.out()); - } -} diff --git a/app/render/audio/audiorendererprocessthread.h b/app/render/audio/audiorendererprocessthread.h deleted file mode 100644 index 91b728c4f..000000000 --- a/app/render/audio/audiorendererprocessthread.h +++ /dev/null @@ -1,61 +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 . - -***/ - -#ifndef AUDIORENDERERPROCESSTHREAD_H -#define AUDIORENDERERPROCESSTHREAD_H - -#include "audiorendererthreadbase.h" - -class AudioRendererProcessor; - -class AudioRendererProcessThread : public AudioRendererThreadBase -{ - Q_OBJECT -public: - AudioRendererProcessThread(AudioRendererProcessor* parent, - const AudioRenderingParams ¶ms); - - bool Queue(const NodeDependency &dep, bool wait, bool sibling); - -public slots: - virtual void Cancel() override; - -protected: - virtual void ProcessLoop() override; - -signals: - void RequestSibling(NodeDependency dep); - - void CachedSamples(const QByteArray& samples, const rational& in, const rational& out); - -private: - AudioRendererProcessor* parent_; - - NodeDependency path_; - - QAtomicInt cancelled_; - - bool sibling_; - -}; - -using AudioRendererProcessThreadPtr = std::shared_ptr; - -#endif // AUDIORENDERERPROCESSTHREAD_H diff --git a/app/render/audio/audiorendererthreadbase.cpp b/app/render/audio/audiorendererthreadbase.cpp deleted file mode 100644 index 7045de398..000000000 --- a/app/render/audio/audiorendererthreadbase.cpp +++ /dev/null @@ -1,69 +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 . - -***/ - -#include "audiorendererthreadbase.h" - -#include - -AudioRendererThreadBase::AudioRendererThreadBase(const AudioRenderingParams ¶ms) : - params_(params) -{ -} - -AudioParams *AudioRendererThreadBase::params() -{ - return ¶ms_; -} - -void AudioRendererThreadBase::run() -{ - // Lock mutex for main loop - mutex_.lock(); - - // Signal that main thread can continue now - WakeCaller(); - - // Main loop (use Cancel() to exit it) - ProcessLoop(); - - // Unlock mutex before exiting - mutex_.unlock(); -} - -void AudioRendererThreadBase::WakeCaller() -{ - // Signal that main thread can continue now - caller_mutex_.lock(); - wait_cond_.wakeAll(); - caller_mutex_.unlock(); -} - -void AudioRendererThreadBase::StartThread(QThread::Priority priority) -{ - caller_mutex_.lock(); - - // Start the thread - QThread::start(priority); - - // Wait for thread to finish completion - wait_cond_.wait(&caller_mutex_); - - caller_mutex_.unlock(); -} diff --git a/app/render/audio/audiorendererthreadbase.h b/app/render/audio/audiorendererthreadbase.h deleted file mode 100644 index b5ddfb8d3..000000000 --- a/app/render/audio/audiorendererthreadbase.h +++ /dev/null @@ -1,65 +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 . - -***/ - -#ifndef AUDIORENDERTHREAD_H -#define AUDIORENDERTHREAD_H - -#include -#include -#include -#include - -#include "audioparams.h" -#include "node/node.h" - -class AudioRendererThreadBase : public QThread -{ - Q_OBJECT -public: - AudioRendererThreadBase(const AudioRenderingParams ¶ms); - - AudioParams* params(); - - void StartThread(Priority priority = InheritPriority); - - virtual void run() override; - -public slots: - virtual void Cancel() = 0; - -protected: - virtual void ProcessLoop() = 0; - - QWaitCondition wait_cond_; - - QMutex mutex_; - - QMutex caller_mutex_; - -private: - void WakeCaller(); - - AudioRenderingParams params_; - -}; - -using AudioRendererThreadPtr = std::shared_ptr; - -#endif // AUDIORENDERTHREAD_H diff --git a/app/render/audio/audioparams.cpp b/app/render/audioparams.cpp similarity index 100% rename from app/render/audio/audioparams.cpp rename to app/render/audioparams.cpp diff --git a/app/render/audio/audioparams.h b/app/render/audioparams.h similarity index 100% rename from app/render/audio/audioparams.h rename to app/render/audioparams.h diff --git a/app/render/backend/CMakeLists.txt b/app/render/backend/CMakeLists.txt index 02026e673..1dda6c477 100644 --- a/app/render/backend/CMakeLists.txt +++ b/app/render/backend/CMakeLists.txt @@ -14,15 +14,22 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +add_subdirectory(opengl) +add_subdirectory(vulkan) + set(OLIVE_SOURCES ${OLIVE_SOURCES} - render/backend/cudabackend.h - render/backend/cudabackend.cpp - render/backend/metalbackend.h - render/backend/metalbackend.cpp - render/backend/openclbackend.h - render/backend/openclbackend.cpp render/backend/renderbackend.h render/backend/renderbackend.cpp + render/backend/audiorenderbackend.h + render/backend/audiorenderbackend.cpp + render/backend/videorenderbackend.h + render/backend/videorenderbackend.cpp + + # FIXME: Remove these + render/backend/videorendererdownloadthread.h + render/backend/videorendererdownloadthread.cpp + render/backend/videorendererprocessthread.h + render/backend/videorendererprocessthread.cpp PARENT_SCOPE ) diff --git a/app/render/backend/audiorenderbackend.cpp b/app/render/backend/audiorenderbackend.cpp new file mode 100644 index 000000000..5b60d8e1d --- /dev/null +++ b/app/render/backend/audiorenderbackend.cpp @@ -0,0 +1,6 @@ +#include "audiorenderbackend.h" + +AudioRenderBackend::AudioRenderBackend() +{ + +} diff --git a/app/render/backend/audiorenderbackend.h b/app/render/backend/audiorenderbackend.h new file mode 100644 index 000000000..a57e61d1b --- /dev/null +++ b/app/render/backend/audiorenderbackend.h @@ -0,0 +1,16 @@ +#ifndef AUDIORENDERBACKEND_H +#define AUDIORENDERBACKEND_H + +#include "renderbackend.h" + +class AudioRenderBackend : public RenderBackend +{ + Q_OBJECT +public: + AudioRenderBackend(); + +public slots: + virtual void InvalidateCache(const rational &start_range, const rational &end_range); +}; + +#endif // AUDIORENDERBACKEND_H diff --git a/app/render/backend/cudabackend.cpp b/app/render/backend/cudabackend.cpp deleted file mode 100644 index 04329de24..000000000 --- a/app/render/backend/cudabackend.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "cudabackend.h" - -CUDABackend::CUDABackend() -{ - -} diff --git a/app/render/backend/cudabackend.h b/app/render/backend/cudabackend.h deleted file mode 100644 index d47cf7807..000000000 --- a/app/render/backend/cudabackend.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef CUDABACKEND_H -#define CUDABACKEND_H - - -class CUDABackend -{ -public: - CUDABackend(); -}; - -#endif // CUDABACKEND_H diff --git a/app/render/backend/metalbackend.cpp b/app/render/backend/metalbackend.cpp deleted file mode 100644 index c33c2983e..000000000 --- a/app/render/backend/metalbackend.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "metalbackend.h" - -MetalBackend::MetalBackend() -{ - -} diff --git a/app/render/backend/metalbackend.h b/app/render/backend/metalbackend.h deleted file mode 100644 index 4123d6497..000000000 --- a/app/render/backend/metalbackend.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef METALBACKEND_H -#define METALBACKEND_H - - -class MetalBackend -{ -public: - MetalBackend(); -}; - -#endif // METALBACKEND_H diff --git a/app/render/backend/openclbackend.cpp b/app/render/backend/openclbackend.cpp deleted file mode 100644 index be38e3c6c..000000000 --- a/app/render/backend/openclbackend.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include "openclbackend.h" - -OpenCLBackend::OpenCLBackend() -{ - -} - -bool OpenCLBackend::Init() -{ - // Get platform and device information - cl_platform_id platform_id = nullptr; - cl_uint ret_num_devices; - cl_uint ret_num_platforms; - cl_int ret = clGetPlatformIDs(1, &platform_id, &ret_num_platforms); - ret = clGetDeviceIDs( platform_id, CL_DEVICE_TYPE_GPU, 1, - &device_id_, &ret_num_devices); - - if (ret != CL_SUCCESS) { - qWarning() << "Failed to find compatible OpenCL device"; - return false; - } - - // Create an OpenCL context - context_ = clCreateContext(nullptr, 1, &device_id_, nullptr, nullptr, &ret); - - // Create a command queue - //command_queue_ = clCreateCommandQueue(context_, device_id_, 0, &ret); - - return true; -} - -void OpenCLBackend::GenerateFrame(const rational &time) -{ - Q_UNUSED(time) - /* - // Copy the lists A and B to their respective memory buffers - cl_int ret = clEnqueueWriteBuffer(command_queue_, a_mem_obj, CL_TRUE, 0, - BITMAP_SZ, source_bmp, 0, NULL, NULL); - - // Set the arguments of the kernel - ret = clSetKernelArg(kernel, 0, sizeof(cl_mem), (void *)&a_mem_obj); - ret = clSetKernelArg(kernel, 1, sizeof(cl_mem), (void *)&c_mem_obj); - - // Execute the OpenCL kernel on the list - size_t global_item_size = BITMAP_SZ; // Process the entire lists - size_t local_item_size = 30; // Divide work items into groups of 64 - ret = clEnqueueNDRangeKernel(command_queue_, kernel, 1, NULL, - &global_item_size, &local_item_size, 0, NULL, NULL); - - if (ret != CL_SUCCESS) { - fprintf(stderr, "Failed to run\n"); - exit(1); - } - - // Read the memory buffer C on the device to the local variable C - ret = clEnqueueReadBuffer(command_queue_, c_mem_obj, CL_TRUE, 0, - BITMAP_SZ, dest_bmp, 0, NULL, NULL); - - // Clean up - ret = clFlush(command_queue_); - ret = clFinish(command_queue_); - */ -} - -void OpenCLBackend::Close() -{ - Decompile(); - - cl_int ret; - - /*ret = clReleaseKernel(kernel); - ret = clReleaseMemObject(a_mem_obj); - ret = clReleaseMemObject(c_mem_obj);*/ - ret = clReleaseCommandQueue(command_queue_); - ret = clReleaseContext(context_); -} - -void OpenCLBackend::Decompile() -{ - clReleaseProgram(program_); -} - -void OpenCLBackend::Compile() -{ - /* - cl_int ret; - - // Create memory buffers on the device for each vector - cl_mem a_mem_obj = clCreateBuffer(context_, CL_MEM_READ_ONLY, BITMAP_SZ, nullptr, &ret); - cl_mem c_mem_obj = clCreateBuffer(context_, CL_MEM_WRITE_ONLY, BITMAP_SZ, nullptr, &ret); - - // Create a program from the kernel source - program_ = clCreateProgramWithSource(context_, - 1, - (const char **)&source_str, - (const size_t *)&source_size, - &ret); - - // Build the program - ret = clBuildProgram(program_, 1, &device_id_, nullptr, nullptr, nullptr); - - if (ret != CL_SUCCESS) { - // Decompile failed, the user will probably want to know why - size_t error_len = 0; - - clGetProgramBuildInfo(program_, device_id_, CL_PROGRAM_BUILD_LOG, 0, nullptr, &error_len); - char* err = new char[error_len]; - clGetProgramBuildInfo(program_, device_id_, CL_PROGRAM_BUILD_LOG, error_len, err, nullptr); - - SetError(err); - //fprintf(stderr, "%s\n", err); - - delete [] err; - } - - // Create the OpenCL kernel - cl_kernel kernel = clCreateKernel(program_, "vector_add", &ret); - */ -} - -void OpenCLBackend::GenerateCode() -{ - if (viewer_node() == nullptr) { - return; - } - - -} diff --git a/app/render/backend/openclbackend.h b/app/render/backend/openclbackend.h deleted file mode 100644 index 0ca332071..000000000 --- a/app/render/backend/openclbackend.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef OPENCLBACKEND_H -#define OPENCLBACKEND_H - -#ifdef __APPLE__ -#include -#else -#include -#endif - -#include "renderbackend.h" - -class OpenCLBackend : public RenderBackend -{ -public: - OpenCLBackend(); - - virtual bool Init() override; - - virtual void GenerateFrame(const rational& time) override; - - virtual void Close() override; - - void Compile(); - -protected: - virtual void Decompile() override; - -private: - void GenerateCode(); - - cl_context context_; - - cl_program program_; - - cl_command_queue command_queue_; - - cl_device_id device_id_; -}; - -#endif // OPENCLBACKEND_H diff --git a/app/render/video/CMakeLists.txt b/app/render/backend/opengl/CMakeLists.txt similarity index 64% rename from app/render/video/CMakeLists.txt rename to app/render/backend/opengl/CMakeLists.txt index 10d5a6691..fe3457fb9 100644 --- a/app/render/video/CMakeLists.txt +++ b/app/render/backend/opengl/CMakeLists.txt @@ -16,13 +16,15 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} - render/video/videorenderer.h - render/video/videorenderer.cpp - render/video/videorendererthreadbase.h - render/video/videorendererthreadbase.cpp - render/video/videorendererdownloadthread.h - render/video/videorendererdownloadthread.cpp - render/video/videorendererprocessthread.h - render/video/videorendererprocessthread.cpp + render/backend/opengl/functions.h + render/backend/opengl/functions.cpp + render/backend/opengl/openglbackend.h + render/backend/opengl/openglbackend.cpp + render/backend/opengl/openglframebuffer.h + render/backend/opengl/openglframebuffer.cpp + render/backend/opengl/openglshader.h + render/backend/opengl/openglshader.cpp + render/backend/opengl/opengltexture.h + render/backend/opengl/opengltexture.cpp PARENT_SCOPE ) diff --git a/app/render/gl/functions.cpp b/app/render/backend/opengl/functions.cpp similarity index 96% rename from app/render/gl/functions.cpp rename to app/render/backend/opengl/functions.cpp index e486a8e5a..bd9fcee7a 100644 --- a/app/render/gl/functions.cpp +++ b/app/render/backend/opengl/functions.cpp @@ -72,7 +72,7 @@ void PrepareToDraw(QOpenGLFunctions* f) { f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); } -void olive::gl::Blit(ShaderPtr pipeline, bool flipped, QMatrix4x4 matrix) { +void olive::gl::Blit(OpenGLShaderPtr pipeline, bool flipped, QMatrix4x4 matrix) { // FIXME: is currentContext() reliable here? QOpenGLFunctions* func = QOpenGLContext::currentContext()->functions(); @@ -121,7 +121,7 @@ void olive::gl::Blit(ShaderPtr pipeline, bool flipped, QMatrix4x4 matrix) { m_vao.destroy(); } -void olive::gl::OCIOBlit(ShaderPtr pipeline, +void olive::gl::OCIOBlit(OpenGLShaderPtr pipeline, GLuint lut, bool flipped, QMatrix4x4 matrix) diff --git a/app/render/gl/functions.h b/app/render/backend/opengl/functions.h similarity index 83% rename from app/render/gl/functions.h rename to app/render/backend/opengl/functions.h index 9f417694e..763b5b1e9 100644 --- a/app/render/gl/functions.h +++ b/app/render/backend/opengl/functions.h @@ -23,7 +23,7 @@ #include -#include "shaderptr.h" +#include "openglshader.h" namespace olive { namespace gl { @@ -43,9 +43,9 @@ namespace gl { * * Transformation matrix to use when drawing (defaults to no transform) */ -void Blit(ShaderPtr pipeline, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4()); +void Blit(OpenGLShaderPtr pipeline, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4()); -void OCIOBlit(ShaderPtr pipeline, GLuint lut, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4()); +void OCIOBlit(OpenGLShaderPtr pipeline, GLuint lut, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4()); } } diff --git a/app/render/backend/opengl/openglbackend.cpp b/app/render/backend/opengl/openglbackend.cpp new file mode 100644 index 000000000..030f4b2ba --- /dev/null +++ b/app/render/backend/opengl/openglbackend.cpp @@ -0,0 +1,232 @@ +#include "openglbackend.h" + +#include + +OpenGLBackend::OpenGLBackend(QOpenGLContext *share_ctx) : + share_ctx_(share_ctx) +{ +} + +OpenGLBackend::~OpenGLBackend() +{ + Close(); +} + +bool OpenGLBackend::Init() +{ + threads_.resize(QThread::idealThreadCount()); + + // Some OpenGL implementations (notably wgl) require the context not to be current before sharing. We block the main + // thread here to prevent QOpenGLWidget trying to reclaim the context before we're done + QSurface* old_surface = share_ctx_->surface(); + share_ctx_->doneCurrent(); + + // Initiate one thread per CPU core + for (int i=0;iSetParameters(VideoRenderingParams(viewer_node()->video_params(), olive::PIX_FMT_RGBA16F, olive::kOffline)); + + // Finally, we can move it to its own thread + processor->moveToThread(thread); + } + + // We've finished creating shared contexts, we can now restore the context to its previous current state + share_ctx_->makeCurrent(old_surface); + + return true; +} + +void OpenGLBackend::GenerateFrame(const rational &time) +{ + Q_UNUSED(time) +} + +void OpenGLBackend::Close() +{ + Decompile(); + + // Clear all cores + for (int i=0;itexture_input()->IsConnected()) { + // Nothing to be done, nothing to compile + return true; + } + + // Traverse node graph compiling where necessary + bool ret = TraverseCompiling(viewer_node()); + + if (ret) { + qDebug() << "Compiled successfully!"; + } else { + qDebug() << "Compile failed:" << GetError(); + } + + return ret; +} + +void OpenGLBackend::Decompile() +{ + foreach (const CompiledNode& info, compiled_nodes_) { + delete info.program; + } + compiled_nodes_.clear(); +} + +bool OpenGLBackend::TraverseCompiling(Node *n) +{ + foreach (NodeParam* param, n->parameters()) { + if (param->type() == NodeParam::kInput && param->IsConnected()) { + NodeOutput* connected_output = static_cast(param)->get_connected_output(); + + // Generate the ID we'd use for this shader + QString output_id = GenerateShaderID(connected_output); + + // Check if we have a shader or not + if (GetShaderFromID(output_id) == nullptr) { + // Since we don't have a shader, compile one now + QString node_code = connected_output->parent()->Code(connected_output); + + // If the node has no code, it mustn't be GPU accelerated + if (!node_code.isEmpty()) { + // Since we have shader code, compile it now + CompiledNode compiled_info; + compiled_info.id = output_id; + + if (!(compiled_info.program = new QOpenGLShaderProgram())) { + SetError("Failed to create OpenGL shader object"); + return false; + } + + if (!compiled_info.program->create()) { + SetError("Failed to create OpenGL shader on device"); + return false; + } + + if (!compiled_info.program->addShaderFromSourceCode(QOpenGLShader::Fragment, node_code)) { + SetError("Failed to add OpenGL shader code"); + return false; + } + + if (compiled_info.program->link()) { + SetError("Failed to compile OpenGL shader"); + return false; + } + + compiled_nodes_.append(compiled_info); + + qDebug() << "Compiled" << compiled_info.id; + } + } + + if (!TraverseCompiling(connected_output->parent())) { + return false; + } + } + } + + return true; +} + +QOpenGLShaderProgram* OpenGLBackend::GetShaderFromID(const QString &id) +{ + foreach (const CompiledNode& info, compiled_nodes_) { + if (info.id == id) { + return info.program; + } + } + + return nullptr; +} + +QString OpenGLBackend::GenerateShaderID(NodeOutput *output) +{ + // Creates a unique identifier for this specific node and this specific output + return QString("%1:%2").arg(output->parent()->id(), output->id()); +} + +OpenGLProcessor::OpenGLProcessor(QOpenGLContext *share_ctx, QObject *parent) : + QObject(parent), + share_ctx_(share_ctx), + ctx_(nullptr), + functions_(nullptr) +{ + surface_.create(); +} + +OpenGLProcessor::~OpenGLProcessor() +{ + surface_.destroy(); +} + +bool OpenGLProcessor::IsStarted() +{ + return ctx_ != nullptr; +} + +void OpenGLProcessor::SetParameters(const VideoRenderingParams &video_params) +{ + video_params_ = video_params; +} + +void OpenGLProcessor::Init() +{ + // Create context object + ctx_ = new QOpenGLContext(); + + // Set share context + ctx_->setShareContext(share_ctx_); + + // Create OpenGL context (automatically destroys any existing if there is one) + if (!ctx_->create()) { + qWarning() << "Failed to create OpenGL context in thread" << thread(); + Close(); + return; + } + + // Make context current on that surface + if (!ctx_->makeCurrent(&surface_)) { + qWarning() << "Failed to makeCurrent() on offscreen surface in thread" << thread(); + Close(); + return; + } + + // Store OpenGL functions instance + functions_ = ctx_->functions(); + + // Set up OpenGL parameters as necessary + functions_->glEnable(GL_BLEND); + UpdateViewportFromParams(); + + buffer_.Create(ctx_); +} + +void OpenGLProcessor::Close() +{ + buffer_.Destroy(); + + functions_ = nullptr; + delete ctx_; +} + +void OpenGLProcessor::UpdateViewportFromParams() +{ + if (functions_ != nullptr && video_params_.is_valid()) { + functions_->glViewport(0, 0, video_params_.effective_width(), video_params_.effective_height()); + } +} diff --git a/app/render/backend/opengl/openglbackend.h b/app/render/backend/opengl/openglbackend.h new file mode 100644 index 000000000..84e7b8fde --- /dev/null +++ b/app/render/backend/opengl/openglbackend.h @@ -0,0 +1,81 @@ +#ifndef OPENGLBACKEND_H +#define OPENGLBACKEND_H + +#include +#include +#include + +#include "../renderbackend.h" +#include "openglframebuffer.h" + +class OpenGLProcessor : public QObject { +public: + OpenGLProcessor(QOpenGLContext* share_ctx, QObject* parent = nullptr); + + virtual ~OpenGLProcessor() override; + + Q_DISABLE_COPY_MOVE(OpenGLProcessor) + + bool IsStarted(); + + void SetParameters(const VideoRenderingParams& video_params); + +public slots: + void Init(); + + void Close(); + +private: + void UpdateViewportFromParams(); + + QOpenGLContext* share_ctx_; + + QOpenGLContext* ctx_; + QOffscreenSurface surface_; + + QOpenGLFunctions* functions_; + + OpenGLFramebuffer buffer_; + + VideoRenderingParams video_params_; +}; + +class OpenGLBackend : public RenderBackend +{ +public: + OpenGLBackend(QOpenGLContext* share_ctx); + + virtual ~OpenGLBackend() override; + + virtual bool Init() override; + + virtual void GenerateFrame(const rational& time) override; + + virtual void Close() override; + +public slots: + virtual bool Compile() override; + + virtual void Decompile() override; + +private: + QOpenGLContext* share_ctx_; + + struct CompiledNode { + QString id; + QOpenGLShaderProgram* program; + }; + + bool TraverseCompiling(Node* n); + + QOpenGLShaderProgram *GetShaderFromID(const QString& id); + + QString GenerateShaderID(NodeOutput* output); + + QList compiled_nodes_; + + QVector threads_; + QVector processors_; +}; + +#endif // OPENGLBACKEND_H diff --git a/app/render/renderframebuffer.cpp b/app/render/backend/opengl/openglframebuffer.cpp similarity index 83% rename from app/render/renderframebuffer.cpp rename to app/render/backend/opengl/openglframebuffer.cpp index b4aa6aa80..ee86e5dd9 100644 --- a/app/render/renderframebuffer.cpp +++ b/app/render/backend/opengl/openglframebuffer.cpp @@ -18,12 +18,12 @@ ***/ -#include "renderframebuffer.h" +#include "openglframebuffer.h" #include #include -RenderFramebuffer::RenderFramebuffer() : +OpenGLFramebuffer::OpenGLFramebuffer() : context_(nullptr), buffer_(0), texture_(nullptr) @@ -31,12 +31,12 @@ RenderFramebuffer::RenderFramebuffer() : } -RenderFramebuffer::~RenderFramebuffer() +OpenGLFramebuffer::~OpenGLFramebuffer() { Destroy(); } -void RenderFramebuffer::Create(QOpenGLContext *ctx) +void OpenGLFramebuffer::Create(QOpenGLContext *ctx) { if (ctx == nullptr) { qWarning() << tr("RenderTexture::Create was passed an invalid context"); @@ -54,7 +54,7 @@ void RenderFramebuffer::Create(QOpenGLContext *ctx) context_->functions()->glGenFramebuffers(1, &buffer_); } -void RenderFramebuffer::Destroy() +void OpenGLFramebuffer::Destroy() { if (context_ != nullptr) { disconnect(context_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Destroy())); @@ -67,12 +67,12 @@ void RenderFramebuffer::Destroy() } } -bool RenderFramebuffer::IsCreated() const +bool OpenGLFramebuffer::IsCreated() const { return (buffer_ > 0); } -void RenderFramebuffer::Bind() +void OpenGLFramebuffer::Bind() { if (context_ == nullptr) { return; @@ -80,7 +80,7 @@ void RenderFramebuffer::Bind() context_->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, buffer_); } -void RenderFramebuffer::Release() +void OpenGLFramebuffer::Release() { if (context_ == nullptr) { return; @@ -88,7 +88,7 @@ void RenderFramebuffer::Release() context_->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } -void RenderFramebuffer::Attach(RenderTexturePtr texture) +void OpenGLFramebuffer::Attach(RenderTexturePtr texture) { if (context_ == nullptr) { return; @@ -98,7 +98,7 @@ void RenderFramebuffer::Attach(RenderTexturePtr texture) AttachInternal(texture_->texture(), false); } -void RenderFramebuffer::AttachBackBuffer(RenderTexturePtr texture) +void OpenGLFramebuffer::AttachBackBuffer(RenderTexturePtr texture) { if (context_ == nullptr) { return; @@ -108,7 +108,7 @@ void RenderFramebuffer::AttachBackBuffer(RenderTexturePtr texture) AttachInternal(texture_->back_texture(), true); } -void RenderFramebuffer::Detach() +void OpenGLFramebuffer::Detach() { if (context_ == nullptr) { return; @@ -129,12 +129,12 @@ void RenderFramebuffer::Detach() texture_ = nullptr; } -const GLuint &RenderFramebuffer::buffer() const +const GLuint &OpenGLFramebuffer::buffer() const { return buffer_; } -void RenderFramebuffer::AttachInternal(GLuint tex, bool clear) +void OpenGLFramebuffer::AttachInternal(GLuint tex, bool clear) { Detach(); diff --git a/app/render/renderframebuffer.h b/app/render/backend/opengl/openglframebuffer.h similarity index 71% rename from app/render/renderframebuffer.h rename to app/render/backend/opengl/openglframebuffer.h index ef13d4a91..c29c08ef1 100644 --- a/app/render/renderframebuffer.h +++ b/app/render/backend/opengl/openglframebuffer.h @@ -18,23 +18,21 @@ ***/ -#ifndef RENDERFRAMEBUFFER_H -#define RENDERFRAMEBUFFER_H +#ifndef OPENGLFRAMEBUFFER_H +#define OPENGLFRAMEBUFFER_H #include -#include "rendertexture.h" +#include "opengltexture.h" -class RenderFramebuffer : public QObject +class OpenGLFramebuffer : public QObject { Q_OBJECT public: - RenderFramebuffer(); - ~RenderFramebuffer(); - RenderFramebuffer(const RenderFramebuffer& other) = delete; - RenderFramebuffer(RenderFramebuffer&& other) = delete; - RenderFramebuffer& operator=(const RenderFramebuffer& other) = delete; - RenderFramebuffer& operator=(RenderFramebuffer&& other) = delete; + OpenGLFramebuffer(); + virtual ~OpenGLFramebuffer() override; + + Q_DISABLE_COPY_MOVE(OpenGLFramebuffer) void Create(QOpenGLContext *ctx); @@ -65,4 +63,4 @@ private: RenderTexturePtr texture_; }; -#endif // RENDERFRAMEBUFFER_H +#endif // OPENGLFRAMEBUFFER_H diff --git a/app/render/backend/opengl/openglshader.cpp b/app/render/backend/opengl/openglshader.cpp new file mode 100644 index 000000000..b1aa9dfa4 --- /dev/null +++ b/app/render/backend/opengl/openglshader.cpp @@ -0,0 +1,120 @@ +#include "openglshader.h" + +OpenGLShader::OpenGLShader() +{ + +} + +OpenGLShaderPtr OpenGLShader::CreateDefault(const QString &function_name, const QString &shader_code) +{ + OpenGLShaderPtr program = std::make_shared(); + + // Add shaders to program + program->addShaderFromSourceCode(QOpenGLShader::Vertex, CodeDefaultVertex()); + program->addShaderFromSourceCode(QOpenGLShader::Fragment, CodeDefaultFragment(function_name, shader_code)); + program->link(); + + return program; +} + +QString OpenGLShader::CodeDefaultFragment(const QString &function_name, const QString &shader_code) +{ + QString frag_code = QStringLiteral("#version 110\n" + "\n" + "#ifdef GL_ES\n" + "precision highp int;\n" + "precision highp float;\n" + "#endif\n" + "\n" + "uniform sampler2D texture;\n" + "uniform bool color_only;\n" + "uniform vec4 color_only_color;\n" + "varying vec2 v_texcoord;\n" + "\n"); + + // Finish the function with the main function + + // Check if additional code was passed to this function, add it here + if (shader_code.isEmpty()) { + + // If not, just add a pure main() function + + frag_code.append(QStringLiteral("\n" + "void main() {\n" + " if (color_only) {\n" + " gl_FragColor = color_only_color;" + " } else {\n" + " vec4 color = texture2D(texture, v_texcoord);\n" + " gl_FragColor = color;\n" + " }\n" + "}\n")); + + } else { + + // 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`. + + frag_code.append(shader_code); + + frag_code.append(QString(QStringLiteral("\n" + "void main() {\n" + " vec4 color = %1(texture2D(texture, v_texcoord));\n" + " gl_FragColor = color;\n" + "}\n")).arg(function_name)); + + } + + return frag_code; +} + +QString OpenGLShader::CodeDefaultVertex() +{ + // Generate vertex shader + return QStringLiteral("#version 110\n" + "\n" + "#ifdef GL_ES\n" + "precision highp int;\n" + "precision highp float;\n" + "#endif\n" + "\n" + "uniform mat4 mvp_matrix;\n" + "\n" + "attribute vec4 a_position;\n" + "attribute vec2 a_texcoord;\n" + "\n" + "varying vec2 v_texcoord;\n" + "\n" + "void main() {\n" + " gl_Position = mvp_matrix * a_position;\n" + " v_texcoord = a_texcoord;\n" + "}\n"); +} + +QString OpenGLShader::CodeAlphaDisassociate(const QString &function_name) +{ + return QString(QStringLiteral("vec4 %1(vec4 col) {\n" + " if (col.a > 0.0) {\n" + " return vec4(col.rgb / col.a, col.a);" + " }\n" + " return col;\n" + "}\n")).arg(function_name); +} + +QString OpenGLShader::CodeAlphaReassociate(const QString &function_name) +{ + return QString(QStringLiteral("vec4 %1(vec4 col) {\n" + " if (col.a > 0.0) {\n" + " return vec4(col.rgb * col.a, col.a);" + " }\n" + " return col;\n" + "}\n")).arg(function_name); +} + +QString OpenGLShader::CodeAlphaAssociate(const QString &function_name) +{ + return QString(QStringLiteral("vec4 %1(vec4 col) {\n" + " return vec4(col.rgb * col.a, col.a);\n" + "}\n")).arg(function_name); +} diff --git a/app/render/backend/opengl/openglshader.h b/app/render/backend/opengl/openglshader.h new file mode 100644 index 000000000..9ada28c7f --- /dev/null +++ b/app/render/backend/opengl/openglshader.h @@ -0,0 +1,28 @@ +#ifndef OPENGLSHADER_H +#define OPENGLSHADER_H + +#include +#include + +class OpenGLShader; +using OpenGLShaderPtr = std::shared_ptr; + +class OpenGLShader : public QOpenGLShaderProgram { +public: + OpenGLShader(); + + static OpenGLShaderPtr CreateDefault(const QString &function_name = QString(), + const QString &shader_code = QString()); + + static QString CodeDefaultFragment(const QString &function_name = QString(), + const QString &shader_code = QString()); + static QString CodeDefaultVertex(); + static QString CodeAlphaDisassociate(const QString& function_name); + static QString CodeAlphaReassociate(const QString& function_name); + static QString CodeAlphaAssociate(const QString& function_name); + +private: + +}; + +#endif // OPENGLSHADER_H diff --git a/app/render/rendertexture.cpp b/app/render/backend/opengl/opengltexture.cpp similarity index 84% rename from app/render/rendertexture.cpp rename to app/render/backend/opengl/opengltexture.cpp index a2b49b665..3a7f710ce 100644 --- a/app/render/rendertexture.cpp +++ b/app/render/backend/opengl/opengltexture.cpp @@ -18,14 +18,14 @@ ***/ -#include "rendertexture.h" +#include "opengltexture.h" #include #include #include "render/pixelservice.h" -RenderTexture::RenderTexture() : +OpenGLTexture::OpenGLTexture() : context_(nullptr), texture_(0), back_texture_(0), @@ -35,22 +35,22 @@ RenderTexture::RenderTexture() : { } -RenderTexture::~RenderTexture() +OpenGLTexture::~OpenGLTexture() { Destroy(); } -bool RenderTexture::IsCreated() const +bool OpenGLTexture::IsCreated() const { return (texture_ != 0); } -void RenderTexture::Create(QOpenGLContext *ctx, int width, int height, const olive::PixelFormat &format, void* data) +void OpenGLTexture::Create(QOpenGLContext *ctx, int width, int height, const olive::PixelFormat &format, void* data) { Create(ctx, width, height, format, kSingleBuffer, data); } -void RenderTexture::Create(QOpenGLContext *ctx, int width, int height, const olive::PixelFormat &format, const RenderTexture::Type &type, void *data) +void OpenGLTexture::Create(QOpenGLContext *ctx, int width, int height, const olive::PixelFormat &format, const OpenGLTexture::Type &type, void *data) { if (ctx == nullptr) { qWarning() << tr("RenderTexture::Create was passed an invalid context"); @@ -75,7 +75,7 @@ void RenderTexture::Create(QOpenGLContext *ctx, int width, int height, const oli } } -void RenderTexture::Destroy() +void OpenGLTexture::Destroy() { if (context_ != nullptr) { disconnect(context_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Destroy())); @@ -90,7 +90,7 @@ void RenderTexture::Destroy() } } -void RenderTexture::Bind() +void OpenGLTexture::Bind() { if (context_ == nullptr) { qWarning() << "RenderTexture::Bind() called with an invalid context"; @@ -100,7 +100,7 @@ void RenderTexture::Bind() context_->functions()->glBindTexture(GL_TEXTURE_2D, texture_); } -void RenderTexture::Release() +void OpenGLTexture::Release() { if (context_ == nullptr) { qWarning() << "RenderTexture::Release() called with an invalid context"; @@ -110,44 +110,44 @@ void RenderTexture::Release() context_->functions()->glBindTexture(GL_TEXTURE_2D, 0); } -const int &RenderTexture::width() const +const int &OpenGLTexture::width() const { return width_; } -const int &RenderTexture::height() const +const int &OpenGLTexture::height() const { return height_; } -const olive::PixelFormat &RenderTexture::format() const +const olive::PixelFormat &OpenGLTexture::format() const { return format_; } -QOpenGLContext *RenderTexture::context() const +QOpenGLContext *OpenGLTexture::context() const { return context_; } -const GLuint &RenderTexture::texture() const +const GLuint &OpenGLTexture::texture() const { return texture_; } -const GLuint &RenderTexture::back_texture() const +const GLuint &OpenGLTexture::back_texture() const { return back_texture_; } -void RenderTexture::SwapFrontAndBack() +void OpenGLTexture::SwapFrontAndBack() { GLuint temp = texture_; texture_ = back_texture_; back_texture_ = temp; } -void RenderTexture::Upload(const void *data) +void OpenGLTexture::Upload(const void *data) { if (!IsCreated()) { qWarning() << tr("RenderTexture::Upload() called while it wasn't created"); @@ -171,7 +171,7 @@ void RenderTexture::Upload(const void *data) Release(); } -uchar *RenderTexture::Download() const +uchar *OpenGLTexture::Download() const { if (!IsCreated()) { qWarning() << tr("RenderTexture::Download() called while it wasn't created"); @@ -203,7 +203,7 @@ uchar *RenderTexture::Download() const return data; } -void RenderTexture::CreateInternal(GLuint* tex, void *data) +void OpenGLTexture::CreateInternal(GLuint* tex, void *data) { QOpenGLFunctions* f = context_->functions(); diff --git a/app/render/rendertexture.h b/app/render/backend/opengl/opengltexture.h similarity index 79% rename from app/render/rendertexture.h rename to app/render/backend/opengl/opengltexture.h index 4d405b3c0..72c59def4 100644 --- a/app/render/rendertexture.h +++ b/app/render/backend/opengl/opengltexture.h @@ -18,15 +18,15 @@ ***/ -#ifndef RENDERTEXTURE_H -#define RENDERTEXTURE_H +#ifndef OPENGLTEXTURE_H +#define OPENGLTEXTURE_H #include #include -#include "pixelformat.h" +#include "render/pixelformat.h" -class RenderTexture : public QObject +class OpenGLTexture : public QObject { Q_OBJECT public: @@ -35,12 +35,10 @@ public: kDoubleBuffer }; - RenderTexture(); - ~RenderTexture(); - RenderTexture(const RenderTexture& other) = delete; - RenderTexture(RenderTexture&& other) = delete; - RenderTexture& operator=(const RenderTexture& other) = delete; - RenderTexture& operator=(RenderTexture&& other) = delete; + OpenGLTexture(); + virtual ~OpenGLTexture() override; + + Q_DISABLE_COPY_MOVE(OpenGLTexture) void Create(QOpenGLContext* ctx, int width, int height, const olive::PixelFormat &format, void *data = nullptr); void Create(QOpenGLContext* ctx, int width, int height, const olive::PixelFormat &format, const Type& type, void *data = nullptr); @@ -86,7 +84,7 @@ private: olive::PixelFormat format_; }; -using RenderTexturePtr = std::shared_ptr; +using RenderTexturePtr = std::shared_ptr; Q_DECLARE_METATYPE(RenderTexturePtr) -#endif // RENDERTEXTURE_H +#endif // OPENGLTEXTURE_H diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp index 61b99341b..28b3b6b15 100644 --- a/app/render/backend/renderbackend.cpp +++ b/app/render/backend/renderbackend.cpp @@ -8,7 +8,6 @@ RenderBackend::RenderBackend() : RenderBackend::~RenderBackend() { - } const QString &RenderBackend::GetError() @@ -16,13 +15,89 @@ const QString &RenderBackend::GetError() return error_; } -void RenderBackend::set_viewer_node(ViewerOutput *viewer_node) +void RenderBackend::SetViewerNode(ViewerOutput *viewer_node) { + if (viewer_node_ != nullptr) { + disconnect(viewer_node_, SIGNAL(TextureChangedBetween(const rational&, const rational&)), this, SLOT(Compile())); + } + viewer_node_ = viewer_node; + if (viewer_node_ != nullptr) { + connect(viewer_node_, SIGNAL(TextureChangedBetween(const rational&, const rational&)), this, SLOT(Compile())); + } + Decompile(); } +void RenderBackend::InvalidateCache(const rational &start_range, const rational &end_range) +{ + if (!params_.is_valid()) { + return; + } + + // Adjust range to min/max values + rational start_range_adj = qMax(rational(0), start_range); + rational end_range_adj = qMin(viewer_node_->Length(), end_range); + + qDebug() << "Cache invalidated between" + << start_range_adj.toDouble() + << "and" + << end_range_adj.toDouble(); + + // Snap start_range to timebase + double start_range_dbl = start_range_adj.toDouble(); + double start_range_numf = start_range_dbl * static_cast(params_.time_base().denominator()); + int64_t start_range_numround = qFloor(start_range_numf/static_cast(params_.time_base().numerator())) * params_.time_base().numerator(); + rational true_start_range(start_range_numround, params_.time_base().denominator()); + + for (rational r=true_start_range;r<=end_range_adj;r+=params_.time_base()) { + // Try to order the queue from closest to the playhead to furthest + rational last_time = last_time_requested_; + + rational diff = r - last_time; + + if (diff < 0) { + // FIXME: Hardcoded number + // If the number is before the playhead, we still prioritize its closeness but not nearly as much (5:1 in this + // example) + diff = qAbs(diff) * 5; + } + + bool contains = false; + bool added = false; + QLinkedList::iterator insert_iterator; + + for (QLinkedList::iterator i = cache_queue_.begin();i != cache_queue_.end();i++) { + rational compare = *i; + + if (!added) { + rational compare_diff = compare - last_time; + + if (compare_diff > diff) { + insert_iterator = i; + added = true; + } + } + + if (compare == r) { + contains = true; + break; + } + } + + if (!contains) { + if (added) { + cache_queue_.insert(insert_iterator, r); + } else { + cache_queue_.append(r); + } + } + } + + CacheNext(); +} + void RenderBackend::SetError(const QString &error) { error_ = error; diff --git a/app/render/backend/renderbackend.h b/app/render/backend/renderbackend.h index 934d3a908..cbbd60834 100644 --- a/app/render/backend/renderbackend.h +++ b/app/render/backend/renderbackend.h @@ -3,28 +3,33 @@ #include "node/output/viewer/viewer.h" -class RenderBackend : QObject +class RenderBackend : public QObject { Q_OBJECT public: RenderBackend(); - virtual ~RenderBackend(); + virtual ~RenderBackend() override; + + Q_DISABLE_COPY_MOVE(RenderBackend) virtual bool Init() = 0; virtual void GenerateFrame(const rational& time) = 0; - virtual void GenerateSamples(const rational& time, const rational& length) = 0; - virtual void Close() = 0; const QString& GetError(); - void set_viewer_node(ViewerOutput* viewer_node); + void SetViewerNode(ViewerOutput* viewer_node); + +public slots: + virtual void InvalidateCache(const rational &start_range, const rational &end_range) = 0; + + virtual bool Compile() = 0; -protected: virtual void Decompile() = 0; +protected: void SetError(const QString& error); ViewerOutput* viewer_node() const; diff --git a/app/render/video/videorenderer.cpp b/app/render/backend/videorenderbackend.cpp similarity index 100% rename from app/render/video/videorenderer.cpp rename to app/render/backend/videorenderbackend.cpp diff --git a/app/render/video/videorenderer.h b/app/render/backend/videorenderbackend.h similarity index 96% rename from app/render/video/videorenderer.h rename to app/render/backend/videorenderbackend.h index 0eae2479f..9a30342d6 100644 --- a/app/render/video/videorenderer.h +++ b/app/render/backend/videorenderbackend.h @@ -18,8 +18,8 @@ ***/ -#ifndef RENDERER_H -#define RENDERER_H +#ifndef VIDEORENDERERBACKEND_H +#define VIDEORENDERERBACKEND_H #include #include @@ -166,8 +166,8 @@ private: RenderTexturePtr master_texture_; rational push_time_; - RenderFramebuffer copy_buffer_; - ShaderPtr copy_pipeline_; + OpenGLFramebuffer copy_buffer_; + OpenGLShaderPtr copy_pipeline_; QMap time_hash_map_; @@ -193,4 +193,4 @@ private slots: }; -#endif // RENDERER_H +#endif // VIDEORENDERERBACKEND_H diff --git a/app/render/video/videorendererdownloadthread.cpp b/app/render/backend/videorendererdownloadthread.cpp similarity index 100% rename from app/render/video/videorendererdownloadthread.cpp rename to app/render/backend/videorendererdownloadthread.cpp diff --git a/app/render/video/videorendererdownloadthread.h b/app/render/backend/videorendererdownloadthread.h similarity index 100% rename from app/render/video/videorendererdownloadthread.h rename to app/render/backend/videorendererdownloadthread.h diff --git a/app/render/video/videorendererprocessthread.cpp b/app/render/backend/videorendererprocessthread.cpp similarity index 100% rename from app/render/video/videorendererprocessthread.cpp rename to app/render/backend/videorendererprocessthread.cpp diff --git a/app/render/video/videorendererprocessthread.h b/app/render/backend/videorendererprocessthread.h similarity index 100% rename from app/render/video/videorendererprocessthread.h rename to app/render/backend/videorendererprocessthread.h diff --git a/app/render/gl/CMakeLists.txt b/app/render/backend/vulkan/CMakeLists.txt similarity index 84% rename from app/render/gl/CMakeLists.txt rename to app/render/backend/vulkan/CMakeLists.txt index c5079ebdf..00002eff9 100644 --- a/app/render/gl/CMakeLists.txt +++ b/app/render/backend/vulkan/CMakeLists.txt @@ -16,10 +16,7 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} - render/gl/functions.h - render/gl/functions.cpp - render/gl/shadergenerators.h - render/gl/shadergenerators.cpp - render/gl/shaderptr.h + render/backend/vulkan/vulkanbackend.h + render/backend/vulkan/vulkanbackend.cpp PARENT_SCOPE ) diff --git a/app/render/backend/vulkan/vulkanbackend.cpp b/app/render/backend/vulkan/vulkanbackend.cpp new file mode 100644 index 000000000..6ce92e589 --- /dev/null +++ b/app/render/backend/vulkan/vulkanbackend.cpp @@ -0,0 +1,6 @@ +#include "vulkanbackend.h" + +VulkanBackend::VulkanBackend() +{ + +} diff --git a/app/render/backend/vulkan/vulkanbackend.h b/app/render/backend/vulkan/vulkanbackend.h new file mode 100644 index 000000000..1f04dcce6 --- /dev/null +++ b/app/render/backend/vulkan/vulkanbackend.h @@ -0,0 +1,11 @@ +#ifndef VULKANBACKEND_H +#define VULKANBACKEND_H + + +class VulkanBackend +{ +public: + VulkanBackend(); +}; + +#endif // VULKANBACKEND_H diff --git a/app/render/colormanager.h b/app/render/colormanager.h index 649355db7..ff91179b1 100644 --- a/app/render/colormanager.h +++ b/app/render/colormanager.h @@ -5,7 +5,6 @@ #include "colorprocessor.h" #include "decoder/frame.h" -#include "render/gl/shadergenerators.h" class ColorManager : public QObject { diff --git a/app/render/gl/shadergenerators.cpp b/app/render/gl/shadergenerators.cpp deleted file mode 100644 index 4274eb6dc..000000000 --- a/app/render/gl/shadergenerators.cpp +++ /dev/null @@ -1,254 +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 . - -***/ - -#include "shadergenerators.h" - -#include - -namespace olive { - -ShaderPtr ShaderGenerator::DefaultPipeline(const QString& function_name, const QString& shader_code) -{ - ShaderPtr program = std::make_shared(); - - // Generate vertex shader - QString vert_shader = "#version 110\n" - "\n" - "#ifdef GL_ES\n" - "precision mediump int;\n" - "precision mediump float;\n" - "#endif\n" - "\n" - "uniform mat4 mvp_matrix;\n" - "\n" - "attribute vec4 a_position;\n" - "attribute vec2 a_texcoord;\n" - "\n" - "varying vec2 v_texcoord;\n" - "\n" - "void main() {\n" - " gl_Position = mvp_matrix * a_position;\n" - " v_texcoord = a_texcoord;\n" - "}\n"; - - // Generate fragment shader - QString frag_shader = "#version 110\n" - "\n" - "#ifdef GL_ES\n" - "precision mediump int;\n" - "precision mediump float;\n" - "#endif\n" - "\n" - "uniform sampler2D texture;\n" - "uniform float opacity;\n" - "uniform bool color_only;\n" - "uniform vec4 color_only_color;\n" - "varying vec2 v_texcoord;\n" - "\n"; - - // Finish the function with the main function - - // Check if additional code was passed to this function, add it here - if (shader_code.isEmpty()) { - - // If not, just add a pure main() function - - frag_shader.append("\n" - "void main() {\n" - " if (color_only) {\n" - " gl_FragColor = color_only_color;" - " } else {\n" - " vec4 color = texture2D(texture, v_texcoord)*opacity;\n" - " gl_FragColor = color;\n" - " }\n" - "}\n"); - - } else { - - // 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`. - - frag_shader.append(shader_code); - - frag_shader.append(QString("\n" - "void main() {\n" - " vec4 color = %1(texture2D(texture, v_texcoord))*opacity;\n" - " gl_FragColor = color;\n" - "}\n").arg(function_name)); - - } - - - - - // Add shaders to program - program->addShaderFromSourceCode(QOpenGLShader::Vertex, vert_shader); - program->addShaderFromSourceCode(QOpenGLShader::Fragment, frag_shader); - program->link(); - - // Set opacity default to 100% - program->bind(); - program->setUniformValue("opacity", 1.0f); - program->release(); - - return program; -} - -QString ShaderGenerator::AlphaDisassociateFunction(const QString &function_name) -{ - return QString("vec4 %1(vec4 col) {\n" - " if (col.a > 0.0) {\n" - " return vec4(col.rgb / col.a, col.a);" - " }\n" - " return col;\n" - "}\n").arg(function_name); -} - -QString ShaderGenerator::AlphaReassociateFunction(const QString &function_name) -{ - return QString("vec4 %1(vec4 col) {\n" - " if (col.a > 0.0) {\n" - " return vec4(col.rgb * col.a, col.a);" - " }\n" - " return col;\n" - "}\n").arg(function_name); -} - -QString ShaderGenerator::AlphaAssociateFunction(const QString &function_name) -{ - return QString("vec4 %1(vec4 col) {\n" - " return vec4(col.rgb * col.a, col.a);\n" - "}\n").arg(function_name); -} - -// copied from source code to OCIODisplay -const int OCIO_LUT3D_EDGE_SIZE = 32; - -// copied from source code to OCIODisplay, expanded from 3*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE -const int OCIO_NUM_3D_ENTRIES = 98304; - -ShaderPtr ShaderGenerator::OCIOPipeline(QOpenGLContext* ctx, - GLuint& lut_texture, - OCIO::ConstProcessorRcPtr processor, - bool alpha_is_associated) -{ - - QOpenGLExtraFunctions* xf = ctx->extraFunctions(); - - // Create LUT texture - xf->glGenTextures(1, &lut_texture); - - // Bind LUT - xf->glBindTexture(GL_TEXTURE_3D, lut_texture); - - // Set texture parameters - xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - - // Allocate storage for texture - xf->glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F_ARB, - OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, - 0, GL_RGB,GL_FLOAT, nullptr); - - // - // SET UP GLSL SHADER - // - - OCIO::GpuShaderDesc shaderDesc; - const char* ocio_func_name = "OCIODisplay"; - shaderDesc.setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_0); - shaderDesc.setFunctionName(ocio_func_name); - shaderDesc.setLut3DEdgeLen(OCIO_LUT3D_EDGE_SIZE); - - // - // COMPUTE 3D LUT - // - - GLfloat* ocio_lut_data = new GLfloat[OCIO_NUM_3D_ENTRIES]; - processor->getGpuLut3D(ocio_lut_data, shaderDesc); - - // Upload LUT data to texture - xf->glTexSubImage3D(GL_TEXTURE_3D, 0, - 0, 0, 0, - OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, - GL_RGB, GL_FLOAT, ocio_lut_data); - - delete [] ocio_lut_data; - - // Create OCIO shader code - QString shader_text(processor->getGpuShaderText(shaderDesc)); - - QString shader_call; - - // Enforce alpha association - if (alpha_is_associated) { - - // If alpha is already associated, we'll need to disassociate and reassociate - shader_text.append("\n"); - - QString disassociate_func_name = "disassoc"; - shader_text.append(AlphaDisassociateFunction(disassociate_func_name)); - - QString reassociate_func_name = "reassoc"; - shader_text.append(AlphaReassociateFunction(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); - - } else { - - // If alpha is not already associated, we can just associate after OCIO - - // Add associate function - QString associate_func_name = "assoc"; - shader_text.append(AlphaAssociateFunction(associate_func_name)); - - // Make OCIO call pass through associate function - shader_call = QString("%2(%1(col, tex2));").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)); - - - // Get pipeline-based shader to inject OCIO shader into - ShaderPtr shader = ShaderGenerator::DefaultPipeline(process_function_name, shader_text); - - // Release LUT - xf->glBindTexture(GL_TEXTURE_3D, 0); - - return shader; -} - -} diff --git a/app/render/gl/shadergenerators.h b/app/render/gl/shadergenerators.h deleted file mode 100644 index d18702c14..000000000 --- a/app/render/gl/shadergenerators.h +++ /dev/null @@ -1,55 +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 . - -***/ - -#ifndef SHADERGENERATORS_H -#define SHADERGENERATORS_H - -#include -namespace OCIO = OCIO_NAMESPACE::v1; - -#include "shaderptr.h" - -/** - * - * Olive standardizes on OpenGL 3.2 Core which has no fixed pipeline. Instead, the pipeline is provided by the - * programmer in the form of a shader. This is a collection of OpenGL shader pipeline generators for use throughout - * Olive. - * - */ - -namespace olive { - -class ShaderGenerator { -public: - static ShaderPtr DefaultPipeline(const QString &function_name = QString(), const QString &shader_code = QString()); - - static ShaderPtr OCIOPipeline(QOpenGLContext *ctx, - GLuint &lut_texture, - OCIO::ConstProcessorRcPtr processor, - bool alpha_is_associated); - - static QString AlphaDisassociateFunction(const QString& function_name); - static QString AlphaReassociateFunction(const QString& function_name); - static QString AlphaAssociateFunction(const QString& function_name); -}; - -} - -#endif // SHADERGENERATORS_H diff --git a/app/render/gl/shaderptr.h b/app/render/gl/shaderptr.h deleted file mode 100644 index 7d2601721..000000000 --- a/app/render/gl/shaderptr.h +++ /dev/null @@ -1,32 +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 . - -***/ - -#ifndef QOPENGLSHADERPROGRAMPTR_H -#define QOPENGLSHADERPROGRAMPTR_H - -#include -#include - -/** - * @brief A simple shared_ptr around QOpenGLShaderProgram to simplify shader creation/destruction - */ -using ShaderPtr = std::shared_ptr; - -#endif // QOPENGLSHADERPROGRAMPTR_H diff --git a/app/render/renderinstance.cpp b/app/render/renderinstance.cpp deleted file mode 100644 index e48531ee6..000000000 --- a/app/render/renderinstance.cpp +++ /dev/null @@ -1,125 +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 . - -***/ - -#include "renderinstance.h" - -#include - -#include "render/gl/shadergenerators.h" - -RenderInstance::RenderInstance(const VideoRenderingParams& params) : - share_ctx_(nullptr), - params_(params) -{ - // Create offscreen surface - surface_.create(); -} - -RenderInstance::~RenderInstance() -{ - // Destroy offscreen surface - surface_.destroy(); -} - -void RenderInstance::SetShareContext(QOpenGLContext *share) -{ - Q_ASSERT(!IsStarted()); - - share_ctx_ = share; -} - -bool RenderInstance::Start() -{ - if (IsStarted()) { - return true; - } - - // Create context object - ctx_ = new QOpenGLContext(); - - // If we're sharing resources, set this up now - if (share_ctx_ != nullptr) { - ctx_->setShareContext(share_ctx_); - } - - // Create OpenGL context (automatically destroys any existing if there is one) - if (!ctx_->create()) { - qWarning() << tr("Failed to create OpenGL context in thread %1").arg(reinterpret_cast(this)); - return false; - } - - // Make context current on that surface - if (!ctx_->makeCurrent(&surface_)) { - qWarning() << tr("Failed to makeCurrent() on offscreen surface in thread %1").arg(reinterpret_cast(this)); - return false; - } - - buffer_.Create(ctx_); - - // Set viewport to the compositing dimensions - ctx_->functions()->glViewport(0, 0, params_.effective_width(), params_.effective_height()); - ctx_->functions()->glEnable(GL_BLEND); - - // Set up default pipeline - default_pipeline_ = olive::ShaderGenerator::DefaultPipeline(); - - return true; -} - -void RenderInstance::Stop() -{ - if (IsStarted()) { - return; - } - - // Destroy pipeline - default_pipeline_ = nullptr; - - // Destroy buffer - buffer_.Destroy(); - - // Destroy context - delete ctx_; -} - -bool RenderInstance::IsStarted() -{ - return buffer_.IsCreated(); -} - -RenderFramebuffer *RenderInstance::buffer() -{ - return &buffer_; -} - -QOpenGLContext *RenderInstance::context() -{ - return ctx_; -} - -const VideoRenderingParams &RenderInstance::params() const -{ - return params_; -} - -ShaderPtr RenderInstance::default_pipeline() const -{ - return default_pipeline_; -} diff --git a/app/render/renderinstance.h b/app/render/renderinstance.h deleted file mode 100644 index 4229b9f83..000000000 --- a/app/render/renderinstance.h +++ /dev/null @@ -1,98 +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 . - -***/ - -#ifndef GLINSTANCE_H -#define GLINSTANCE_H - -#include -#include -#include - -#include "render/gl/shaderptr.h" -#include "render/renderframebuffer.h" -#include "render/rendermodes.h" -#include "render/videoparams.h" - -/** - * @brief An object containing all resources necessary for each thread to support hardware accelerated rendering - * - * RenderInstance contains everything that Nodes will need to draw with on a per-thread basis. - * - * Due to its usage of QOffscreenSurface, a RenderInstance instance must be constructed in the main (GUI) thread. From - * there it is safe to call Start() on in another thread. - */ -class RenderInstance : public QObject -{ -public: - RenderInstance(const VideoRenderingParams ¶ms); - - virtual ~RenderInstance() override; - - /** - * @brief Deleted copy constructor - */ - RenderInstance(const RenderInstance& other) = delete; - - /** - * @brief Deleted move constructor - */ - RenderInstance(RenderInstance&& other) = delete; - - /** - * @brief Deleted copy assignment - */ - RenderInstance& operator=(const RenderInstance& other) = delete; - - /** - * @brief Deleted move assignment - */ - RenderInstance& operator=(RenderInstance&& other) = delete; - - void SetShareContext(QOpenGLContext* share); - - bool Start(); - - void Stop(); - - bool IsStarted(); - - RenderFramebuffer* buffer(); - - QOpenGLContext* context(); - - const VideoRenderingParams& params() const; - - ShaderPtr default_pipeline() const; - -private: - QOpenGLContext* ctx_; - - QOpenGLContext* share_ctx_; - - QOffscreenSurface surface_; - - RenderFramebuffer buffer_; - - VideoRenderingParams params_; - - ShaderPtr default_pipeline_; -}; - -#endif // GLINSTANCE_H diff --git a/app/render/video/videorendererthreadbase.cpp b/app/render/video/videorendererthreadbase.cpp deleted file mode 100644 index 602d7a229..000000000 --- a/app/render/video/videorendererthreadbase.cpp +++ /dev/null @@ -1,83 +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 . - -***/ - -#include "videorendererthreadbase.h" - -#include - -VideoRendererThreadBase::VideoRendererThreadBase(QOpenGLContext *share_ctx, const VideoRenderingParams ¶ms) : - share_ctx_(share_ctx), - render_instance_(params) -{ - connect(share_ctx_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Cancel())); -} - -RenderInstance *VideoRendererThreadBase::render_instance() -{ - return &render_instance_; -} - -void VideoRendererThreadBase::run() -{ - // Lock mutex for main loop - mutex_.lock(); - - render_instance_.SetShareContext(share_ctx_); - - // Allocate and create resources - bool started = render_instance_.Start(); - - // Signal that main thread can continue now - WakeCaller(); - - if (started) { - - // Main loop (use Cancel() to exit it) - ProcessLoop(); - - } - - // Free all resources - render_instance_.Stop(); - - // Unlock mutex before exiting - mutex_.unlock(); -} - -void VideoRendererThreadBase::WakeCaller() -{ - // Signal that main thread can continue now - caller_mutex_.lock(); - wait_cond_.wakeAll(); - caller_mutex_.unlock(); -} - -void VideoRendererThreadBase::StartThread(QThread::Priority priority) -{ - caller_mutex_.lock(); - - // Start the thread - QThread::start(priority); - - // Wait for thread to finish completion - wait_cond_.wait(&caller_mutex_); - - caller_mutex_.unlock(); -} diff --git a/app/render/video/videorendererthreadbase.h b/app/render/video/videorendererthreadbase.h deleted file mode 100644 index 592017d3d..000000000 --- a/app/render/video/videorendererthreadbase.h +++ /dev/null @@ -1,67 +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 . - -***/ - -#ifndef RENDERTHREAD_H -#define RENDERTHREAD_H - -#include -#include -#include -#include - -#include "node/node.h" -#include "render/renderinstance.h" - -class VideoRendererThreadBase : public QThread -{ - Q_OBJECT -public: - VideoRendererThreadBase(QOpenGLContext* share_ctx, const VideoRenderingParams& params); - - RenderInstance* render_instance(); - - void StartThread(Priority priority = InheritPriority); - - virtual void run() override; - -public slots: - virtual void Cancel() = 0; - -protected: - virtual void ProcessLoop() = 0; - - QWaitCondition wait_cond_; - - QMutex mutex_; - - QMutex caller_mutex_; - -private: - void WakeCaller(); - - QOpenGLContext* share_ctx_; - - RenderInstance render_instance_; - -}; - -using RendererThreadPtr = std::shared_ptr; - -#endif // RENDERTHREAD_H diff --git a/app/widget/audiomonitor/audiomonitor.cpp b/app/widget/audiomonitor/audiomonitor.cpp index 977fe78d6..ef381432c 100644 --- a/app/widget/audiomonitor/audiomonitor.cpp +++ b/app/widget/audiomonitor/audiomonitor.cpp @@ -78,7 +78,7 @@ void AudioMonitor::paintEvent(QPaintEvent *) qreal log_val = QAudio::convertVolume(i, QAudio::DecibelVolumeScale, QAudio::LogarithmicVolumeScale); QRect db_marking_rect = db_labels_rect; - db_marking_rect.adjust(0, db_labels_rect.y() + db_labels_rect.height() - qRound(log_val * db_labels_rect.height()), 0, 0); + db_marking_rect.adjust(0, db_labels_rect.height() - qRound(log_val * db_labels_rect.height()), 0, 0); db_marking_rect.setHeight(fm.height()); // Prevent any dB markings overlapping diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index eccab0b0b..fb5451cab 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -161,6 +161,7 @@ void ViewerWidget::ConnectViewerNode(ViewerOutput *node) } video_renderer_->SetViewerNode(viewer_node_); + opengl_backend_.SetViewerNode(viewer_node_); } void ViewerWidget::DisconnectViewerNode() diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index 62ba637d2..9d16c9f77 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -30,9 +30,7 @@ #include "common/rational.h" #include "node/output/viewer/viewer.h" -#include "render/audio/audiorenderer.h" -#include "render/video/videorenderer.h" -#include "render/backend/openclbackend.h" +#include "render/backend/opengl/openglbackend.h" #include "viewerglwidget.h" #include "viewersizer.h" #include "widget/playbackcontrols/playbackcontrols.h" @@ -112,12 +110,8 @@ private: void PushScrubbedAudio(); - VideoRendererProcessor* video_renderer_; - - AudioRendererProcessor* audio_renderer_; - // FIXME: Test code only - OpenCLBackend opencl_backend_; + OpenGLBackend opengl_backend_; // End test code ViewerSizer* sizer_; diff --git a/app/widget/viewer/viewerglwidget.h b/app/widget/viewer/viewerglwidget.h index 1ce6f9eea..969339562 100644 --- a/app/widget/viewer/viewerglwidget.h +++ b/app/widget/viewer/viewerglwidget.h @@ -24,7 +24,7 @@ #include #include "render/colormanager.h" -#include "render/gl/shaderptr.h" +#include "render/backend/opengl/openglshader.h" /** * @brief The inner display/rendering widget of a Viewer class. @@ -143,7 +143,7 @@ private: * * Retrieved every initializeGL() in order to stay up to date when new contexts are generated. */ - ShaderPtr pipeline_; + OpenGLShaderPtr pipeline_; /** * @brief OCIO LUT texture used for conversions