diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h
index 6e32f1c9c..b498f4b68 100644
--- a/app/node/output/track/track.h
+++ b/app/node/output/track/track.h
@@ -217,11 +217,6 @@ public:
return waveform_;
}
- QMutex* waveform_lock()
- {
- return &waveform_lock_;
- }
-
static const double kTrackHeightDefault;
static const double kTrackHeightMinimum;
static const double kTrackHeightInterval;
@@ -297,7 +292,6 @@ private:
bool locked_;
AudioVisualWaveform waveform_;
- QMutex waveform_lock_;
private slots:
void BlockConnected(NodeEdgePtr edge);
diff --git a/app/render/CMakeLists.txt b/app/render/CMakeLists.txt
index 44b590ee6..1b3c2b9fc 100644
--- a/app/render/CMakeLists.txt
+++ b/app/render/CMakeLists.txt
@@ -46,6 +46,8 @@ set(OLIVE_SOURCES
render/rendermanager.h
render/rendermanager.cpp
render/rendermodes.h
+ render/renderprocessor.h
+ render/renderprocessor.cpp
render/shaderinfo.h
render/videoparams.h
render/videoparams.cpp
diff --git a/app/render/backend/CMakeLists.txt b/app/render/backend/CMakeLists.txt
index 8dc59590c..65c369e52 100644
--- a/app/render/backend/CMakeLists.txt
+++ b/app/render/backend/CMakeLists.txt
@@ -18,5 +18,9 @@ add_subdirectory(opengl)
set(OLIVE_SOURCES
${OLIVE_SOURCES}
+ render/backend/rendercontext.cpp
+ render/backend/rendercontext.h
+ render/backend/rendercontextthreadwrapper.cpp
+ render/backend/rendercontextthreadwrapper.h
PARENT_SCOPE
)
diff --git a/app/render/backend/opengl/CMakeLists.txt b/app/render/backend/opengl/CMakeLists.txt
index cb8f1b674..9464d26bd 100644
--- a/app/render/backend/opengl/CMakeLists.txt
+++ b/app/render/backend/opengl/CMakeLists.txt
@@ -16,19 +16,7 @@
set(OLIVE_SOURCES
${OLIVE_SOURCES}
- render/backend/opengl/openglcolorprocessor.h
- render/backend/opengl/openglcolorprocessor.cpp
- render/backend/opengl/openglframebuffer.h
- render/backend/opengl/openglframebuffer.cpp
- render/backend/opengl/openglproxy.h
- render/backend/opengl/openglproxy.cpp
- render/backend/opengl/openglrenderfunctions.h
- render/backend/opengl/openglrenderfunctions.cpp
- render/backend/opengl/openglshader.h
- render/backend/opengl/openglshader.cpp
- render/backend/opengl/opengltexture.h
- render/backend/opengl/opengltexture.cpp
- render/backend/opengl/opengltexturecache.h
- render/backend/opengl/opengltexturecache.cpp
+ render/backend/opengl/openglcontext.cpp
+ render/backend/opengl/openglcontext.h
PARENT_SCOPE
)
diff --git a/app/render/backend/opengl/openglcolorprocessor.cpp b/app/render/backend/opengl/openglcolorprocessor.cpp
deleted file mode 100644
index bc0993f4f..000000000
--- a/app/render/backend/opengl/openglcolorprocessor.cpp
+++ /dev/null
@@ -1,90 +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 "openglcolorprocessor.h"
-
-#include
-#include
-
-#include "openglrenderfunctions.h"
-
-OLIVE_NAMESPACE_ENTER
-
-void OpenGLColorProcessor::Enable(QOpenGLContext *context, bool alpha_is_associated)
-{
- if (IsEnabled()) {
- return;
- }
-
- context_ = context;
-
- pipeline_ = OpenGLShader::CreateOCIO(context_,
- ocio_lut_,
- GetProcessor(),
- alpha_is_associated);
-
- connect(context_, &QOpenGLContext::aboutToBeDestroyed, this, &OpenGLColorProcessor::ClearTexture, Qt::DirectConnection);
-}
-
-bool OpenGLColorProcessor::IsEnabled() const
-{
- return ocio_lut_;
-}
-
-OpenGLShaderPtr OpenGLColorProcessor::pipeline() const
-{
- return pipeline_;
-}
-
-void OpenGLColorProcessor::ProcessOpenGL(bool flipped, const QMatrix4x4& matrix)
-{
- OpenGLRenderFunctions::OCIOBlit(pipeline_, ocio_lut_, flipped, matrix);
-}
-
-void OpenGLColorProcessor::ClearTexture()
-{
- if (IsEnabled()) {
- // Clean up OCIO LUT texture and shader
- context_->functions()->glDeleteTextures(1, &ocio_lut_);
-
- disconnect(context_, &QOpenGLContext::aboutToBeDestroyed, this, &OpenGLColorProcessor::ClearTexture);
-
- ocio_lut_ = 0;
- pipeline_ = nullptr;
- }
-}
-
-OpenGLColorProcessor::OpenGLColorProcessor(ColorManager* config, const QString &source_space, const ColorTransform &dest_space) :
- ColorProcessor(config, source_space, dest_space),
- ocio_lut_(0)
-{
-}
-
-OpenGLColorProcessor::~OpenGLColorProcessor()
-{
- ClearTexture();
-}
-
-OpenGLColorProcessorPtr OpenGLColorProcessor::Create(ColorManager *config, const QString &source_space, const ColorTransform &dest_space)
-{
- return std::make_shared(config, source_space, dest_space);
-}
-
-OLIVE_NAMESPACE_EXIT
diff --git a/app/render/backend/opengl/openglcolorprocessor.h b/app/render/backend/opengl/openglcolorprocessor.h
deleted file mode 100644
index bd8e6dc9c..000000000
--- a/app/render/backend/opengl/openglcolorprocessor.h
+++ /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 .
-
-***/
-
-#ifndef OPENGLCOLORPROCESSOR_H
-#define OPENGLCOLORPROCESSOR_H
-
-#include "openglshader.h"
-#include "render/colorprocessor.h"
-
-OLIVE_NAMESPACE_ENTER
-
-class OpenGLColorProcessor;
-using OpenGLColorProcessorPtr = std::shared_ptr;
-
-class OpenGLColorProcessor : public QObject, public ColorProcessor
-{
- Q_OBJECT
-public:
- OpenGLColorProcessor(ColorManager *config,
- const QString& input,
- const ColorTransform& dest);
-
- virtual ~OpenGLColorProcessor() override;
-
- static OpenGLColorProcessorPtr Create(ColorManager* config,
- const QString& input,
- const ColorTransform& dest);
-
- void Enable(QOpenGLContext* context, bool alpha_is_associated);
- bool IsEnabled() const;
-
- OpenGLShaderPtr pipeline() const;
-
- void ProcessOpenGL(bool flipped = false, const QMatrix4x4& matrix = QMatrix4x4());
-
-private:
- QOpenGLContext* context_;
-
- GLuint ocio_lut_;
-
- OpenGLShaderPtr pipeline_;
-
-private slots:
- void ClearTexture();
-
-};
-
-using OpenGLColorProcessorCache = QHash;
-
-OLIVE_NAMESPACE_EXIT
-
-#endif // OPENGLCOLORPROCESSOR_H
diff --git a/app/render/backend/opengl/openglcontext.cpp b/app/render/backend/opengl/openglcontext.cpp
new file mode 100644
index 000000000..5a81f4719
--- /dev/null
+++ b/app/render/backend/opengl/openglcontext.cpp
@@ -0,0 +1,195 @@
+/***
+
+ 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 "openglcontext.h"
+
+#include
+
+OLIVE_NAMESPACE_ENTER
+
+OpenGLContext::OpenGLContext(QObject* parent) :
+ RenderContext(parent)
+{
+}
+
+OpenGLContext::~OpenGLContext()
+{
+}
+
+bool OpenGLContext::Init()
+{
+ surface_.create();
+
+ context_ = new QOpenGLContext();
+ if (!context_->create()) {
+ qCritical() << "Failed to create OpenGL context";
+ return false;
+ }
+
+ context_->moveToThread(this->thread());
+
+ return true;
+}
+
+void OpenGLContext::PostInit()
+{
+ // Make context current on that surface
+ if (!context_->makeCurrent(&surface_)) {
+ qCritical() << "Failed to makeCurrent() on offscreen surface in thread" << thread();
+ return;
+ }
+
+ // Store OpenGL functions instance
+ functions_ = context_->functions();
+ functions_->glBlendFunc(GL_ONE, GL_ZERO);
+}
+
+void OpenGLContext::Destroy()
+{
+ delete context_;
+ surface_.destroy();
+}
+
+QVariant OpenGLContext::CreateTexture(const VideoParams &p, void *data, int linesize)
+{
+ GLuint texture;
+ functions_->glGenTextures(1, &texture);
+ texture_params_.insert(texture, p);
+
+ functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize);
+
+ functions_->glTexImage2D(GL_TEXTURE_2D, 0, GetInternalFormat(p.format()),
+ p.width(), p.height(), 0, GetPixelFormat(p.format()),
+ GetPixelType(p.format()), data);
+
+ functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+
+ return texture;
+}
+
+void OpenGLContext::DestroyTexture(QVariant texture)
+{
+ GLuint t = texture.value();
+ functions_->glDeleteTextures(1, &t);
+ texture_params_.remove(t);
+}
+
+void OpenGLContext::UploadToTexture(QVariant texture, void *data, int linesize)
+{
+ GLuint t = texture.value();
+ const VideoParams& p = texture_params_.value(t);
+
+ functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize);
+
+ functions_->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
+ p.effective_width(), p.effective_height(),
+ GetPixelFormat(p.format()), GetPixelType(p.format()),
+ data);
+
+ functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+}
+
+void OpenGLContext::DownloadFromTexture(QVariant texture, void *data, int linesize)
+{
+ GLuint t = texture.value();
+ const VideoParams& p = texture_params_.value(t);
+
+ functions_->glPixelStorei(GL_PACK_ROW_LENGTH, linesize);
+
+ functions_->glReadPixels(0,
+ 0,
+ p.width(),
+ p.height(),
+ GetPixelFormat(p.format()),
+ GetPixelType(p.format()),
+ data);
+
+ functions_->glPixelStorei(GL_PACK_ROW_LENGTH, 0);
+}
+
+VideoParams OpenGLContext::GetParamsFromTexture(QVariant texture)
+{
+ GLuint t = texture.value();
+
+ return texture_params_.value(t);
+}
+
+GLint OpenGLContext::GetInternalFormat(PixelFormat::Format format)
+{
+ switch (format) {
+ case PixelFormat::PIX_FMT_RGB8:
+ return GL_RGB8;
+ case PixelFormat::PIX_FMT_RGBA8:
+ return GL_RGBA8;
+ case PixelFormat::PIX_FMT_RGB16U:
+ return GL_RGB16;
+ case PixelFormat::PIX_FMT_RGBA16U:
+ return GL_RGBA16;
+ case PixelFormat::PIX_FMT_RGB16F:
+ return GL_RGB16F;
+ case PixelFormat::PIX_FMT_RGBA16F:
+ return GL_RGBA16F;
+ case PixelFormat::PIX_FMT_RGB32F:
+ return GL_RGB32F;
+ case PixelFormat::PIX_FMT_RGBA32F:
+ return GL_RGBA32F;
+
+ case PixelFormat::PIX_FMT_INVALID:
+ case PixelFormat::PIX_FMT_COUNT:
+ break;
+ }
+
+ return GL_INVALID_VALUE;
+}
+
+GLenum OpenGLContext::GetPixelFormat(PixelFormat::Format format)
+{
+ if (PixelFormat::FormatHasAlphaChannel(format)) {
+ return GL_RGBA;
+ } else {
+ return GL_RGB;
+ }
+}
+
+GLenum OpenGLContext::GetPixelType(PixelFormat::Format format)
+{
+ switch (format) {
+ case PixelFormat::PIX_FMT_RGB8:
+ case PixelFormat::PIX_FMT_RGBA8:
+ return GL_UNSIGNED_BYTE;
+ case PixelFormat::PIX_FMT_RGB16U:
+ case PixelFormat::PIX_FMT_RGBA16U:
+ return GL_UNSIGNED_SHORT;
+ case PixelFormat::PIX_FMT_RGB16F:
+ case PixelFormat::PIX_FMT_RGBA16F:
+ return GL_HALF_FLOAT;
+ case PixelFormat::PIX_FMT_RGB32F:
+ case PixelFormat::PIX_FMT_RGBA32F:
+ return GL_FLOAT;
+
+ case PixelFormat::PIX_FMT_INVALID:
+ case PixelFormat::PIX_FMT_COUNT:
+ break;
+ }
+
+ return GL_INVALID_VALUE;
+}
+
+OLIVE_NAMESPACE_EXIT
diff --git a/app/render/backend/opengl/openglcontext.h b/app/render/backend/opengl/openglcontext.h
new file mode 100644
index 000000000..9c56f428f
--- /dev/null
+++ b/app/render/backend/opengl/openglcontext.h
@@ -0,0 +1,76 @@
+/***
+
+ 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 OPENGLCONTEXT_H
+#define OPENGLCONTEXT_H
+
+#include
+#include
+#include
+
+#include "render/backend/rendercontext.h"
+
+OLIVE_NAMESPACE_ENTER
+
+class OpenGLContext : public RenderContext
+{
+ Q_OBJECT
+public:
+ OpenGLContext(QObject* parent = nullptr);
+
+ virtual ~OpenGLContext() override;
+
+ virtual bool Init() override;
+
+public slots:
+ virtual void PostInit() override;
+
+ virtual void Destroy() override;
+
+ virtual QVariant CreateTexture(const VideoParams& param, void* data, int linesize) override;
+
+ virtual void DestroyTexture(QVariant texture) override;
+
+ virtual void UploadToTexture(QVariant texture, void* data, int linesize) override;
+
+ virtual void DownloadFromTexture(QVariant texture, void* data, int linesize) override;
+
+ virtual VideoParams GetParamsFromTexture(QVariant texture) override;
+
+private:
+ static GLint GetInternalFormat(PixelFormat::Format format);
+
+ static GLenum GetPixelFormat(PixelFormat::Format format);
+
+ static GLenum GetPixelType(PixelFormat::Format format);
+
+ QOpenGLContext* context_;
+
+ QOpenGLFunctions* functions_;
+
+ QOffscreenSurface surface_;
+
+ QMap texture_params_;
+
+};
+
+OLIVE_NAMESPACE_EXIT
+
+#endif // OPENGLCONTEXT_H
diff --git a/app/render/backend/opengl/openglframebuffer.cpp b/app/render/backend/opengl/openglframebuffer.cpp
deleted file mode 100644
index e31ce50b3..000000000
--- a/app/render/backend/opengl/openglframebuffer.cpp
+++ /dev/null
@@ -1,153 +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 "openglframebuffer.h"
-
-#include
-#include
-
-OLIVE_NAMESPACE_ENTER
-
-OpenGLFramebuffer::OpenGLFramebuffer() :
- context_(nullptr),
- buffer_(0),
- texture_(nullptr)
-{
-}
-
-OpenGLFramebuffer::~OpenGLFramebuffer()
-{
- Destroy();
-}
-
-void OpenGLFramebuffer::Create(QOpenGLContext *ctx)
-{
- if (ctx == nullptr) {
- qWarning() << "OpenGLFramebuffer::Create was passed an invalid context";
- return;
- }
-
- // Free any previous framebuffer
- Destroy();
-
- context_ = ctx;
-
- connect(context_, &QOpenGLContext::aboutToBeDestroyed, this, &OpenGLFramebuffer::Destroy);
-
- // Create framebuffer object
- context_->functions()->glGenFramebuffers(1, &buffer_);
-}
-
-void OpenGLFramebuffer::Destroy()
-{
- if (context_ != nullptr) {
- disconnect(context_, &QOpenGLContext::aboutToBeDestroyed, this, &OpenGLFramebuffer::Destroy);
-
- context_->functions()->glDeleteFramebuffers(1, &buffer_);
-
- buffer_ = 0;
-
- context_ = nullptr;
- }
-}
-
-bool OpenGLFramebuffer::IsCreated() const
-{
- return (buffer_ > 0);
-}
-
-void OpenGLFramebuffer::Bind()
-{
- if (context_ == nullptr) {
- return;
- }
- context_->functions()->glBindFramebuffer(GL_FRAMEBUFFER, buffer_);
-}
-
-void OpenGLFramebuffer::Release()
-{
- if (context_ == nullptr) {
- return;
- }
- context_->functions()->glBindFramebuffer(GL_FRAMEBUFFER, 0);
-}
-
-void OpenGLFramebuffer::Attach(OpenGLTexture *texture, bool clear)
-{
- if (context_ == nullptr) {
- return;
- }
-
- Detach();
-
- texture_ = texture;
-
- QOpenGLFunctions* f = context_->functions();
-
- // bind framebuffer for attaching
- f->glBindFramebuffer(GL_FRAMEBUFFER, buffer_);
-
- context_->extraFunctions()->glFramebufferTexture2D(
- GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_->texture(), 0
- );
-
- if (clear) {
- context_->functions()->glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
- context_->functions()->glClear(GL_COLOR_BUFFER_BIT);
- }
-
- // release framebuffer
- f->glBindFramebuffer(GL_FRAMEBUFFER, 0);
-}
-
-void OpenGLFramebuffer::Attach(OpenGLTexturePtr texture, bool clear)
-{
- Attach(texture.get(), clear);
-}
-
-void OpenGLFramebuffer::Detach()
-{
- if (context_ == nullptr) {
- return;
- }
-
- if (texture_) {
- QOpenGLFunctions* f = context_->functions();
-
- // bind framebuffer for attaching
- f->glBindFramebuffer(GL_FRAMEBUFFER, buffer_);
-
- context_->extraFunctions()->glFramebufferTexture2D(
- GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0
- );
-
- // release framebuffer
- f->glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
- texture_ = nullptr;
- }
-}
-
-const GLuint &OpenGLFramebuffer::buffer() const
-{
- return buffer_;
-}
-
-OLIVE_NAMESPACE_EXIT
diff --git a/app/render/backend/opengl/openglframebuffer.h b/app/render/backend/opengl/openglframebuffer.h
deleted file mode 100644
index e7d8de9b4..000000000
--- a/app/render/backend/opengl/openglframebuffer.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 OPENGLFRAMEBUFFER_H
-#define OPENGLFRAMEBUFFER_H
-
-#include
-
-#include "opengltexture.h"
-
-OLIVE_NAMESPACE_ENTER
-
-class OpenGLFramebuffer : public QObject
-{
- Q_OBJECT
-public:
- OpenGLFramebuffer();
- virtual ~OpenGLFramebuffer() override;
-
- void Create(QOpenGLContext *ctx);
-
- bool IsCreated() const;
-
- void Bind();
-
- void Release();
-
- void Attach(OpenGLTexture* texture, bool clear = false);
- void Attach(OpenGLTexturePtr texture, bool clear = false);
-
- void Detach();
-
- const GLuint& buffer() const;
-
-public slots:
- void Destroy();
-
-private:
- QOpenGLContext* context_;
-
- GLuint buffer_;
-
- OpenGLTexture* texture_;
-};
-
-OLIVE_NAMESPACE_EXIT
-
-#endif // OPENGLFRAMEBUFFER_H
diff --git a/app/render/backend/opengl/openglproxy.cpp b/app/render/backend/opengl/openglproxy.cpp
deleted file mode 100644
index 1db21e0b3..000000000
--- a/app/render/backend/opengl/openglproxy.cpp
+++ /dev/null
@@ -1,567 +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 "openglproxy.h"
-
-#include
-
-#include "common/clamp.h"
-#include "core.h"
-#include "node/block/transition/transition.h"
-#include "node/node.h"
-#include "openglcolorprocessor.h"
-#include "openglrenderfunctions.h"
-#include "render/colormanager.h"
-#include "render/pixelformat.h"
-
-OLIVE_NAMESPACE_ENTER
-
-OpenGLProxy* OpenGLProxy::instance_ = nullptr;
-
-OpenGLProxy::OpenGLProxy(QObject *parent) :
- QObject(parent),
- ctx_(nullptr),
- functions_(nullptr)
-{
- surface_.create();
-}
-
-OpenGLProxy::~OpenGLProxy()
-{
- Close();
-
- surface_.destroy();
-}
-
-void OpenGLProxy::CreateInstance()
-{
- instance_ = new OpenGLProxy();
-
- QThread* proxy_thread = new QThread();
- proxy_thread->start(QThread::IdlePriority);
- instance_->moveToThread(proxy_thread);
-
- if (!instance_->Init()) {
- DestroyInstance();
- }
-}
-
-void OpenGLProxy::DestroyInstance()
-{
- if (instance_) {
- instance_->thread()->quit();
- instance_->thread()->wait();
- instance_->thread()->deleteLater();
- instance_->deleteLater();
- instance_ = nullptr;
- }
-}
-
-bool OpenGLProxy::Init()
-{
- // Create context object
- ctx_ = new QOpenGLContext();
-
- // Create OpenGL context (automatically destroys any existing if there is one)
- if (!ctx_->create()) {
- qWarning() << "Failed to create OpenGL context in thread" << thread();
- return false;
- }
-
- ctx_->moveToThread(this->thread());
-
- // The rest of the initialization needs to occur in the other thread, so we signal for it to start
- QMetaObject::invokeMethod(this, "FinishInit", Qt::QueuedConnection);
-
- return true;
-}
-
-QVariant OpenGLProxy::FrameToValue(FramePtr frame, StreamPtr stream, const VideoParams& params, const RenderMode::Mode& mode)
-{
- VideoStreamPtr video_stream = std::static_pointer_cast(stream);
-
- // Set up OCIO context
- QString colorspace_match = video_stream->get_colorspace_match_string();
-
- OpenGLColorProcessorPtr color_processor = std::static_pointer_cast(color_cache_.value(colorspace_match));
-
- if (!color_processor) {
- color_processor = OpenGLColorProcessor::Create(video_stream->footage()->project()->color_manager(),
- video_stream->colorspace(),
- video_stream->footage()->project()->color_manager()->GetReferenceColorSpace());
- color_cache_.insert(colorspace_match, color_processor);
- }
-
- ColorManager::OCIOMethod ocio_method = ColorManager::GetOCIOMethodForMode(mode);
-
- // OCIO's CPU conversion is more accurate, so for online we render on CPU but offline we render GPU
- if (ocio_method == ColorManager::kOCIOAccurate) {
- bool has_alpha = PixelFormat::FormatHasAlphaChannel(frame->format());
-
- // Convert frame to float for OCIO
- frame = PixelFormat::ConvertPixelFormat(frame,
- has_alpha
- ? PixelFormat::PIX_FMT_RGBA32F
- : PixelFormat::PIX_FMT_RGB32F);
-
- // If alpha is associated, disassociate for the color transform
- if (has_alpha && video_stream->premultiplied_alpha()) {
- ColorManager::DisassociateAlpha(frame);
- }
-
- // Perform color transform
- color_processor->ConvertFrame(frame);
-
- // Associate alpha
- if (has_alpha) {
- if (video_stream->premultiplied_alpha()) {
- ColorManager::ReassociateAlpha(frame);
- } else {
- ColorManager::AssociateAlpha(frame);
- }
- }
- }
-
- OpenGLTextureCache::ReferencePtr footage_tex_ref = texture_cache_.Get(ctx_, frame);
-
- if (ocio_method == ColorManager::kOCIOFast) {
- if (!color_processor->IsEnabled()) {
- color_processor->Enable(ctx_, video_stream->premultiplied_alpha());
- }
-
- VideoParams frame_params = frame->video_params();
-
- PixelFormat::Format texture_fmt;
- if (PixelFormat::FormatHasAlphaChannel(frame_params.format())) {
- texture_fmt = PixelFormat::GetFormatWithAlphaChannel(params.format());
- } else {
- texture_fmt = PixelFormat::GetFormatWithoutAlphaChannel(params.format());
- }
-
- VideoParams dest_params(frame_params.width(),
- frame_params.height(),
- texture_fmt,
- frame_params.pixel_aspect_ratio(),
- frame_params.interlacing(),
- frame_params.divider());
-
- // Create destination texture
- OpenGLTextureCache::ReferencePtr associated_tex_ref = texture_cache_.Get(ctx_, dest_params);
-
- buffer_.Attach(associated_tex_ref->texture(), true);
- buffer_.Bind();
- footage_tex_ref->texture()->Bind();
-
- // Set viewport for texture size
- functions_->glViewport(0, 0, associated_tex_ref->texture()->width(), associated_tex_ref->texture()->height());
-
- // Blit old texture to new texture through OCIO shader
- color_processor->ProcessOpenGL();
-
- footage_tex_ref->texture()->Release();
- buffer_.Release();
- buffer_.Detach();
-
- footage_tex_ref = associated_tex_ref;
- }
-
- return QVariant::fromValue(footage_tex_ref);
-}
-
-QVariant OpenGLProxy::PreCachedFrameToValue(FramePtr frame)
-{
- return QVariant::fromValue(texture_cache_.Get(ctx_, frame));
-}
-
-OpenGLShaderPtr OpenGLProxy::ResolveShaderFromCache(const Node *node, const QString &shader_id)
-{
- // Make a composite of the node ID and the shader ID (if applicable)
- QString full_shader_id = QStringLiteral("%1:%2").arg(node->id(), shader_id);
- OpenGLShaderPtr shader = shader_cache_.value(full_shader_id);
-
- if (!shader) {
- // Since we have shader code, compile it now
- ShaderCode code = node->GetShaderCode(shader_id);
- QString vert_code = code.vert_code();
- QString frag_code = code.frag_code();
-
- if (frag_code.isEmpty() && vert_code.isEmpty()) {
- qWarning() << "No shader code found for" << node->id() << "- operation will be a no-op";
- }
-
- if (frag_code.isEmpty()) {
- frag_code = OpenGLShader::CodeDefaultFragment();
- }
-
- if (vert_code.isEmpty()) {
- vert_code = OpenGLShader::CodeDefaultVertex();
- }
-
- shader = OpenGLShader::Create();
- if (shader
- && shader->create()
- && shader->addShaderFromSourceCode(QOpenGLShader::Fragment, frag_code)
- && shader->addShaderFromSourceCode(QOpenGLShader::Vertex, vert_code)
- && shader->link()) {
- shader_cache_.insert(full_shader_id, shader);
- } else {
- qWarning() << "Failed to compile shader for" << node->id();
- shader = nullptr;
- }
- }
-
- return shader;
-}
-
-void OpenGLProxy::Close()
-{
- shader_cache_.clear();
- buffer_.Destroy();
- copy_pipeline_ = nullptr;
- functions_ = nullptr;
- delete ctx_;
- ctx_ = nullptr;
-}
-
-QVariant OpenGLProxy::RunNodeAccelerated(const Node *node,
- const TimeRange &range,
- const ShaderJob &job,
- const VideoParams& params)
-{
- // If this node is iterative, we'll pick up which input here
- GLuint iterative_input = 0;
- QList textures_to_bind;
- bool input_textures_have_alpha = false;
-
- OpenGLShaderPtr shader = ResolveShaderFromCache(node, job.GetShaderID());
-
- if (!shader) {
- return QVariant();
- }
-
- shader->bind();
-
- NodeValueMap::const_iterator it;
- for (it=job.GetValues().constBegin(); it!=job.GetValues().constEnd(); it++) {
- // See if the shader has takes this parameter as an input
- int variable_location = shader->uniformLocation(it.key());
-
- if (variable_location == -1) {
- continue;
- }
-
- // See if this value corresponds to an input (NOTE: it may not and this may be null)
- NodeInput* corresponding_input = node->GetInputWithID(it.key());
-
- // This variable is used in the shader, let's set it
- const QVariant& value = it.value().data();
-
- NodeParam::DataType data_type = (it.value().type() != NodeParam::kNone)
- ? it.value().type()
- : corresponding_input->data_type();
-
- switch (data_type) {
- case NodeInput::kInt:
- // kInt technically specifies a LongLong, but OpenGL doesn't support those. This may lead to
- // over/underflows if the number is large enough, but the likelihood of that is quite low.
- shader->setUniformValue(variable_location, value.toInt());
- break;
- case NodeInput::kFloat:
- // kFloat technically specifies a double but as above, OpenGL doesn't support those.
- shader->setUniformValue(variable_location, value.toFloat());
- break;
- case NodeInput::kVec2:
- if (corresponding_input && corresponding_input->IsArray()) {
- QVector nv = value.value< QVector >();
- QVector a(nv.size());
-
- for (int j=0;j();
- }
-
- shader->setUniformValueArray(variable_location, a.constData(), a.size());
-
- int count_location = shader->uniformLocation(QStringLiteral("%1_count").arg(it.key()));
- if (count_location > -1) {
- shader->setUniformValue(count_location, a.size());
- }
- } else {
- shader->setUniformValue(variable_location, value.value());
- }
- break;
- case NodeInput::kVec3:
- shader->setUniformValue(variable_location, value.value());
- break;
- case NodeInput::kVec4:
- shader->setUniformValue(variable_location, value.value());
- break;
- case NodeInput::kMatrix:
- shader->setUniformValue(variable_location, value.value());
- break;
- case NodeInput::kCombo:
- shader->setUniformValue(variable_location, value.value());
- break;
- case NodeInput::kColor:
- {
- Color color = value.value();
-
- shader->setUniformValue(variable_location, color.red(), color.green(), color.blue(), color.alpha());
- break;
- }
- case NodeInput::kBoolean:
- shader->setUniformValue(variable_location, value.toBool());
- break;
- case NodeInput::kBuffer:
- case NodeInput::kTexture:
- {
- OpenGLTextureCache::ReferencePtr texture = value.value();
-
- if (texture) {
- if (PixelFormat::FormatHasAlphaChannel(texture->texture()->format())) {
- input_textures_have_alpha = true;
- }
- }
-
- // Set value to bound texture
- shader->setUniformValue(variable_location, textures_to_bind.size());
-
- // If this texture binding is the iterative input, set it here
- if (corresponding_input && corresponding_input == job.GetIterativeInput()) {
- iterative_input = textures_to_bind.size();
- }
-
- GLuint tex_id = texture ? texture->texture()->texture() : 0;
- textures_to_bind.append(tex_id);
-
- // Set enable flag if shader wants it
- int enable_param_location = shader->uniformLocation(QStringLiteral("%1_enabled").arg(it.key()));
- if (enable_param_location > -1) {
- shader->setUniformValue(enable_param_location,
- tex_id > 0);
- }
-
- if (tex_id > 0) {
- // Set texture resolution if shader wants it
- int res_param_location = shader->uniformLocation(QStringLiteral("%1_resolution").arg(it.key()));
- if (res_param_location > -1) {
- int adjusted_width = texture->texture()->width() * texture->texture()->divider();
-
- // Adjust virtual width by pixel aspect if necessary
- if (texture->texture()->params().pixel_aspect_ratio() != 1
- || params.pixel_aspect_ratio() != 1) {
- double relative_pixel_aspect = texture->texture()->params().pixel_aspect_ratio().toDouble() / params.pixel_aspect_ratio().toDouble();
-
- adjusted_width = qRound(static_cast(adjusted_width) * relative_pixel_aspect);
- }
-
- shader->setUniformValue(res_param_location,
- adjusted_width,
- static_cast(texture->texture()->height() * texture->texture()->divider()));
- }
- }
- break;
- }
- case NodeInput::kSamples:
- case NodeInput::kText:
- case NodeInput::kRational:
- case NodeInput::kFont:
- case NodeInput::kFile:
- case NodeInput::kDecimal:
- case NodeInput::kNumber:
- case NodeInput::kString:
- case NodeInput::kVector:
- case NodeInput::kShaderJob:
- case NodeInput::kSampleJob:
- case NodeInput::kGenerateJob:
- case NodeInput::kFootage:
- case NodeInput::kNone:
- case NodeInput::kAny:
- break;
- }
- }
-
- // Provide some standard args
- shader->setUniformValue("ove_resolution",
- static_cast(params.width()),
- static_cast(params.height()));
-
- shader->release();
-
- // Create the output textures
- PixelFormat::Format output_format = (input_textures_have_alpha || job.GetAlphaChannelRequired())
- ? PixelFormat::GetFormatWithAlphaChannel(params.format())
- : PixelFormat::GetFormatWithoutAlphaChannel(params.format());
- VideoParams output_params(params.width(),
- params.height(),
- params.time_base(),
- output_format,
- params.pixel_aspect_ratio(),
- params.interlacing(),
- params.divider());
-
- int real_iteration_count;
- if (job.GetIterationCount() > 1 && job.GetIterativeInput()) {
- real_iteration_count = job.GetIterationCount();
- } else {
- real_iteration_count = 1;
- }
-
- OpenGLTextureCache::ReferencePtr dst_refs[2];
- dst_refs[0] = texture_cache_.Get(ctx_, output_params);
-
- // If this node requires multiple iterations, get a texture for it too
- if (real_iteration_count > 1) {
- dst_refs[1] = texture_cache_.Get(ctx_, output_params);
- }
-
- // Some nodes use multiple iterations for optimization
- OpenGLTextureCache::ReferencePtr input_tex, output_tex;
-
- // Set up OpenGL parameters as necessary
- functions_->glViewport(0, 0, params.effective_width(), params.effective_height());
-
- // Bind all textures
- for (int i=0; iglActiveTexture(GL_TEXTURE0 + i);
- functions_->glBindTexture(GL_TEXTURE_2D, textures_to_bind.at(i));
- OpenGLRenderFunctions::PrepareToDraw(functions_);
- }
-
- for (int iteration=0; iterationbind();
- shader->setUniformValue("ove_iteration", iteration);
- shader->release();
-
- // Replace iterative input
- if (iteration == 0) {
- output_tex = dst_refs[0];
- } else {
- input_tex = dst_refs[(iteration+1)%2];
- output_tex = dst_refs[iteration%2];
-
- functions_->glActiveTexture(GL_TEXTURE0 + iterative_input);
- functions_->glBindTexture(GL_TEXTURE_2D, input_tex->texture()->texture());
- OpenGLRenderFunctions::PrepareToDraw(functions_);
- }
-
- buffer_.Attach(output_tex->texture(), true);
- buffer_.Bind();
-
- // Blit this texture through this shader
- OpenGLRenderFunctions::Blit(shader);
-
- buffer_.Release();
- buffer_.Detach();
- }
-
- // Release any textures we bound before
- for (int i=textures_to_bind.size()-1; i>=0; i--) {
- functions_->glActiveTexture(GL_TEXTURE0 + i);
- functions_->glBindTexture(GL_TEXTURE_2D, 0);
- }
-
- return QVariant::fromValue(output_tex);
-}
-
-void OpenGLProxy::TextureToBuffer(const QVariant& tex_in,
- FramePtr frame,
- const QMatrix4x4& matrix)
-{
- OpenGLTextureCache::ReferencePtr texture = tex_in.value();
-
- if (!texture) {
- return;
- }
-
- OpenGLTextureCache::ReferencePtr download_tex;
-
- if (!frame->is_allocated()) {
- // If the frame isn't allocated, we'll assume that we're allocating it to the texture dimensions
- frame->set_video_params(texture->texture()->params());
- frame->allocate();
- }
-
- functions_->glViewport(0, 0, frame->width(), frame->height());
-
- if (frame->width() != texture->texture()->width()
- || frame->height() != texture->texture()->height()) {
-
- // Resize the texture if necessary
- OpenGLTextureCache::ReferencePtr resized = texture_cache_.Get(ctx_, frame->video_params());
-
- buffer_.Attach(resized->texture(), true);
- buffer_.Bind();
-
- texture->texture()->Bind();
-
- // Blit to this new texture
- OpenGLRenderFunctions::Blit(copy_pipeline_, false, matrix);
-
- texture->texture()->Release();
-
- buffer_.Release();
- buffer_.Detach();
-
- download_tex = resized;
-
- } else {
-
- download_tex = texture;
-
- }
-
- buffer_.Attach(download_tex->texture());
- buffer_.Bind();
-
- functions_->glPixelStorei(GL_PACK_ROW_LENGTH, frame->linesize_pixels());
-
- functions_->glReadPixels(0,
- 0,
- frame->width(),
- frame->height(),
- OpenGLRenderFunctions::GetPixelFormat(frame->format()),
- OpenGLRenderFunctions::GetPixelType(frame->format()),
- frame->data());
-
- functions_->glPixelStorei(GL_PACK_ROW_LENGTH, 0);
-
- buffer_.Release();
- buffer_.Detach();
-}
-
-void OpenGLProxy::FinishInit()
-{
- // Make context current on that surface
- if (!ctx_->makeCurrent(&surface_)) {
- qWarning() << "Failed to makeCurrent() on offscreen surface in thread" << thread();
- return;
- }
-
- // Store OpenGL functions instance
- functions_ = ctx_->functions();
- functions_->glBlendFunc(GL_ONE, GL_ZERO);
-
- buffer_.Create(ctx_);
-
- copy_pipeline_ = OpenGLShader::CreateDefault();
-}
-
-OLIVE_NAMESPACE_EXIT
diff --git a/app/render/backend/opengl/openglproxy.h b/app/render/backend/opengl/openglproxy.h
deleted file mode 100644
index ff59f76b9..000000000
--- a/app/render/backend/opengl/openglproxy.h
+++ /dev/null
@@ -1,122 +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 OPENGLPROXY_H
-#define OPENGLPROXY_H
-
-#include
-#include
-
-#include "common/timerange.h"
-#include "node/value.h"
-#include "openglcolorprocessor.h"
-#include "openglframebuffer.h"
-#include "opengltexturecache.h"
-#include "render/shaderinfo.h"
-
-OLIVE_NAMESPACE_ENTER
-
-class OpenGLProxy : public QObject
-{
- Q_OBJECT
-public:
- OpenGLProxy(QObject* parent = nullptr);
-
- virtual ~OpenGLProxy() override;
-
- static void CreateInstance();
-
- static void DestroyInstance();
-
- static OpenGLProxy* instance()
- {
- return instance_;
- }
-
- /**
- * @brief Initialize OpenGL instance in whatever thread this object is a part of
- *
- * This function creates a context (shared with share_ctx provided in the constructor) as well as various other
- * OpenGL thread-specific objects necessary for rendering. This function should only ever be called from the main
- * thread (i.e. the thread where share_ctx is current on) but AFTER this object has been pushed to its thread with
- * moveToThread(). If this function is called from a different thread, it could fail or even segfault on some
- * platforms.
- *
- * The reason this function must be called in the main thread (rather than initializing asynchronously in a separate
- * thread) is because different platforms have different rules about creating a share context with a context that
- * is still "current" in another thread. While some implementations do allow this, Windows OpenGL (wgl) explicitly
- * forbids it and other platforms/drivers will segfault attempting it. While we can obviously call "doneCurrent", I
- * haven't found any reliable way to prevent the main thread from making it current again before initialization is
- * complete other than blocking it entirely.
- *
- * To get around this, we create all share contexts in the main thread and then move them to the other thread
- * afterwards (which is completely legal). While annoying, this gets around the issue listed above by both preventing
- * the main thread from using the context during initialization and preventing more than one shared context being made
- * at the same time (which may or may not actually make a difference).
- */
- bool Init();
-
- void Close();
-
-public slots:
- QVariant RunNodeAccelerated(const OLIVE_NAMESPACE::Node *node,
- const OLIVE_NAMESPACE::TimeRange &range,
- const OLIVE_NAMESPACE::ShaderJob &job,
- const OLIVE_NAMESPACE::VideoParams ¶ms);
-
- void TextureToBuffer(const QVariant& texture,
- OLIVE_NAMESPACE::FramePtr frame,
- const QMatrix4x4& matrix);
-
- QVariant FrameToValue(OLIVE_NAMESPACE::FramePtr frame,
- OLIVE_NAMESPACE::StreamPtr stream,
- const OLIVE_NAMESPACE::VideoParams ¶ms,
- const OLIVE_NAMESPACE::RenderMode::Mode &mode);
-
- QVariant PreCachedFrameToValue(OLIVE_NAMESPACE::FramePtr frame);
-
-private:
- OpenGLShaderPtr ResolveShaderFromCache(const Node* node, const QString &shader_id);
-
- QOpenGLContext* ctx_;
- QOffscreenSurface surface_;
-
- QOpenGLFunctions* functions_;
-
- OpenGLFramebuffer buffer_;
-
- OpenGLColorProcessorCache color_cache_;
-
- OpenGLShaderPtr copy_pipeline_;
-
- QHash shader_cache_;
-
- OpenGLTextureCache texture_cache_;
-
- static OpenGLProxy* instance_;
-
-private slots:
- void FinishInit();
-
-};
-
-OLIVE_NAMESPACE_EXIT
-
-#endif // OPENGLPROXY_H
diff --git a/app/render/backend/opengl/openglrenderfunctions.cpp b/app/render/backend/opengl/openglrenderfunctions.cpp
deleted file mode 100644
index fcdc36a64..000000000
--- a/app/render/backend/opengl/openglrenderfunctions.cpp
+++ /dev/null
@@ -1,232 +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 "openglrenderfunctions.h"
-
-#include
-#include
-#include
-
-OLIVE_NAMESPACE_ENTER
-
-const QVector blit_vertices = {
- -1.0f, -1.0f, 0.0f,
- 1.0f, -1.0f, 0.0f,
- 1.0f, 1.0f, 0.0f,
-
- -1.0f, -1.0f, 0.0f,
- -1.0f, 1.0f, 0.0f,
- 1.0f, 1.0f, 0.0f
-};
-
-const QVector blit_texcoords = {
- 0.0f, 0.0f,
- 1.0f, 0.0f,
- 1.0f, 1.0f,
-
- 0.0f, 0.0f,
- 0.0f, 1.0f,
- 1.0f, 1.0f
-};
-
-const QVector flipped_blit_texcoords = {
- 0.0f, 1.0f,
- 1.0f, 1.0f,
- 1.0f, 0.0f,
-
- 0.0f, 1.0f,
- 0.0f, 0.0f,
- 1.0f, 0.0f
-};
-
-/**
- * @brief Set up texture parameters and mipmap for drawing
- *
- * Internal function used just before drawing to allow mipmapped bilinear filtering when drawing textures small.
- *
- * @param f
- *
- * Currently active QOpenGLFunctions object (use context()->functions() if unsure).
- */
-void OpenGLRenderFunctions::PrepareToDraw(QOpenGLFunctions* f)
-{
- f->glGenerateMipmap(GL_TEXTURE_2D);
- f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
- f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-}
-
-GLint OpenGLRenderFunctions::GetInternalFormat(const PixelFormat::Format &format)
-{
- switch (format) {
- case PixelFormat::PIX_FMT_RGB8:
- return GL_RGB8;
- case PixelFormat::PIX_FMT_RGBA8:
- return GL_RGBA8;
- case PixelFormat::PIX_FMT_RGB16U:
- return GL_RGB16;
- case PixelFormat::PIX_FMT_RGBA16U:
- return GL_RGBA16;
- case PixelFormat::PIX_FMT_RGB16F:
- return GL_RGB16F;
- case PixelFormat::PIX_FMT_RGBA16F:
- return GL_RGBA16F;
- case PixelFormat::PIX_FMT_RGB32F:
- return GL_RGB32F;
- case PixelFormat::PIX_FMT_RGBA32F:
- return GL_RGBA32F;
-
- case PixelFormat::PIX_FMT_INVALID:
- case PixelFormat::PIX_FMT_COUNT:
- break;
- }
-
- return GL_INVALID_VALUE;
-}
-
-GLenum OpenGLRenderFunctions::GetPixelFormat(const PixelFormat::Format &format)
-{
- if (PixelFormat::FormatHasAlphaChannel(format)) {
- return GL_RGBA;
- } else {
- return GL_RGB;
- }
-}
-
-GLenum OpenGLRenderFunctions::GetPixelType(const PixelFormat::Format &format)
-{
- switch (format) {
- case PixelFormat::PIX_FMT_RGB8:
- case PixelFormat::PIX_FMT_RGBA8:
- return GL_UNSIGNED_BYTE;
- case PixelFormat::PIX_FMT_RGB16U:
- case PixelFormat::PIX_FMT_RGBA16U:
- return GL_UNSIGNED_SHORT;
- case PixelFormat::PIX_FMT_RGB16F:
- case PixelFormat::PIX_FMT_RGBA16F:
- return GL_HALF_FLOAT;
- case PixelFormat::PIX_FMT_RGB32F:
- case PixelFormat::PIX_FMT_RGBA32F:
- return GL_FLOAT;
-
- case PixelFormat::PIX_FMT_INVALID:
- case PixelFormat::PIX_FMT_COUNT:
- break;
- }
-
- return GL_INVALID_VALUE;
-}
-
-void OpenGLRenderFunctions::Blit(OpenGLShaderPtr pipeline, bool flipped, QMatrix4x4 matrix)
-{
- Blit(pipeline.get(), flipped, matrix);
-}
-
-void OpenGLRenderFunctions::Blit(OpenGLShader *pipeline, bool flipped, QMatrix4x4 matrix)
-{
- Blit(pipeline,
- GL_TRIANGLES,
- blit_vertices,
- flipped ? flipped_blit_texcoords : blit_texcoords,
- matrix);
-}
-
-void OpenGLRenderFunctions::Blit(OpenGLShader *pipeline, GLenum mode, const QVector &vert, const QVector &tex, QMatrix4x4 matrix)
-{
- QOpenGLFunctions* func = QOpenGLContext::currentContext()->functions();
-
- PrepareToDraw(func);
-
- QOpenGLVertexArrayObject m_vao;
- m_vao.create();
- m_vao.bind();
-
- QOpenGLBuffer m_vbo;
- m_vbo.create();
- m_vbo.bind();
- m_vbo.allocate(vert.constData(), vert.size() * sizeof(GLfloat));
- m_vbo.release();
-
- QOpenGLBuffer m_vbo2;
- m_vbo2.create();
- m_vbo2.bind();
- m_vbo2.allocate(tex.constData(), tex.size() * sizeof(GLfloat));
- m_vbo2.release();
-
- pipeline->bind();
-
- pipeline->setUniformValue("ove_mvpmat", matrix);
- pipeline->setUniformValue("ove_maintex", 0);
-
- int vertex_location = pipeline->attributeLocation("a_position");
- m_vbo.bind();
- func->glEnableVertexAttribArray(vertex_location);
- func->glVertexAttribPointer(vertex_location, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
- m_vbo.release();
-
- int tex_location = pipeline->attributeLocation("a_texcoord");
- m_vbo2.bind();
- func->glEnableVertexAttribArray(tex_location);
- func->glVertexAttribPointer(tex_location, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
- m_vbo2.release();
-
- // (Size / 3) because we assume each GLfloat has an XYZ pair
- func->glDrawArrays(mode, 0, blit_vertices.size() / 3);
-
- pipeline->release();
-
- m_vbo2.destroy();
- m_vbo.destroy();
- m_vao.release();
- m_vao.destroy();
-}
-
-void OpenGLRenderFunctions::OCIOBlit(OpenGLShaderPtr pipeline, GLuint lut, bool flipped, QMatrix4x4 matrix)
-{
- OCIOBlit(pipeline.get(), lut, flipped, matrix);
-}
-
-void OpenGLRenderFunctions::OCIOBlit(OpenGLShader *pipeline,
- GLuint lut,
- bool flipped,
- QMatrix4x4 matrix)
-{
- QOpenGLContext* ctx = QOpenGLContext::currentContext();
- QOpenGLExtraFunctions* xf = ctx->extraFunctions();
-
- xf->glActiveTexture(GL_TEXTURE1);
- xf->glBindTexture(GL_TEXTURE_3D, lut);
- xf->glActiveTexture(GL_TEXTURE0);
-
- pipeline->bind();
-
- pipeline->setUniformValue("ove_ociolut", 1);
-
- Blit(pipeline, flipped, matrix);
-
- pipeline->release();
-
- xf->glActiveTexture(GL_TEXTURE1);
- xf->glBindTexture(GL_TEXTURE_3D, 0);
- xf->glActiveTexture(GL_TEXTURE0);
-}
-
-OLIVE_NAMESPACE_EXIT
diff --git a/app/render/backend/opengl/openglrenderfunctions.h b/app/render/backend/opengl/openglrenderfunctions.h
deleted file mode 100644
index 7e31987f7..000000000
--- a/app/render/backend/opengl/openglrenderfunctions.h
+++ /dev/null
@@ -1,70 +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 OPENGLFUNCTIONS_H
-#define OPENGLFUNCTIONS_H
-
-#include
-#include
-#include
-
-#include "openglshader.h"
-#include "render/pixelformat.h"
-
-OLIVE_NAMESPACE_ENTER
-
-class OpenGLRenderFunctions {
-public:
- /**
- * @brief Draw texture on screen
- *
- * @param pipeline
- *
- * Shader to use for the texture drawing
- *
- * @param flipped
- *
- * Draw the texture vertically flipped (defaults to FALSE)
- *
- * @param matrix
- *
- * Transformation matrix to use when drawing (defaults to no transform)
- */
- static void Blit(OpenGLShaderPtr pipeline, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4());
- static void Blit(OpenGLShader* pipeline, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4());
- static void Blit(OpenGLShader* pipeline, GLenum mode, const QVector& vert,
- const QVector& tex, QMatrix4x4 matrix = QMatrix4x4());
-
- static void OCIOBlit(OpenGLShaderPtr pipeline, GLuint lut, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4());
- static void OCIOBlit(OpenGLShader* pipeline, GLuint lut, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4());
-
- static void PrepareToDraw(QOpenGLFunctions* f);
-
- static GLint GetInternalFormat(const PixelFormat::Format& format);
-
- static GLenum GetPixelFormat(const PixelFormat::Format& format);
-
- static GLenum GetPixelType(const PixelFormat::Format& format);
-
-};
-
-OLIVE_NAMESPACE_EXIT
-
-#endif // OPENGLFUNCTIONS_H
diff --git a/app/render/backend/opengl/openglshader.cpp b/app/render/backend/opengl/openglshader.cpp
deleted file mode 100644
index 7e76f619a..000000000
--- a/app/render/backend/opengl/openglshader.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 "openglshader.h"
-
-#include
-OLIVE_NAMESPACE_ENTER
-
-OpenGLShaderPtr OpenGLShader::Create()
-{
- return std::make_shared();
-}
-
-OpenGLShaderPtr OpenGLShader::CreateDefault(const QString &function_name, const QString &shader_code)
-{
- OpenGLShaderPtr program = Create();
-
- // Add shaders to program
- program->addShaderFromSourceCode(QOpenGLShader::Vertex, CodeDefaultVertex());
- program->addShaderFromSourceCode(QOpenGLShader::Fragment, CodeDefaultFragment(function_name, shader_code));
- program->link();
-
- return program;
-}
-
-// copied from source code to OCIODisplay
-const int OCIO_LUT3D_EDGE_SIZE = 64;
-
-// copied from source code to OCIODisplay, expanded from 3*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE
-const int OCIO_NUM_3D_ENTRIES = 3*OCIO_LUT3D_EDGE_SIZE*OCIO_LUT3D_EDGE_SIZE*OCIO_LUT3D_EDGE_SIZE;
-
-OpenGLShaderPtr OpenGLShader::CreateOCIO(QOpenGLContext* ctx,
- GLuint& lut_texture,
- OCIO::ConstProcessorRcPtr processor,
- bool alpha_is_associated)
-{
- QOpenGLExtraFunctions* xf = ctx->extraFunctions();
-
- // Set up shader description
- OCIO::GpuShaderDesc shaderDesc;
- const char* ocio_func_name = "OCIODisplay";
- shaderDesc.setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_3);
- shaderDesc.setFunctionName(ocio_func_name);
- shaderDesc.setLut3DEdgeLen(OCIO_LUT3D_EDGE_SIZE);
-
- // Compute LUT
- std::vector ocio_lut_data(OCIO_NUM_3D_ENTRIES);
- processor->getGpuLut3D(&ocio_lut_data[0], shaderDesc);
-
- // Create LUT texture
- xf->glGenTextures(1, &lut_texture);
-
- // Bind LUT
- xf->glActiveTexture(GL_TEXTURE1);
- 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,
- OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE,
- 0, GL_RGB, GL_FLOAT, &ocio_lut_data[0]);
-
- // Create OCIO shader code
- QString shader_text;
-
- // Workaround since OCIO doesn't support the GLSL version we use
- shader_text.append(QStringLiteral("#define texture2D texture\n"
- "#define texture3D texture\n"));
-
- // Append OCIO shader code
- shader_text.append(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(CodeAlphaDisassociate(disassociate_func_name));
-
- QString reassociate_func_name = "reassoc";
- shader_text.append(CodeAlphaReassociate(reassociate_func_name));
-
- // Make OCIO call pass through disassociate and reassociate function
- shader_call = QStringLiteral("%3(%1(%2(col), ove_ociolut));").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(CodeAlphaAssociate(associate_func_name));
-
- // Make OCIO call pass through associate function
- shader_call = QStringLiteral("%2(%1(col, ove_ociolut));").arg(ocio_func_name, associate_func_name);
-
- }
-
- // Add process() function, which GetPipeline() will call if specified
- QString process_function_name = "process";
- shader_text.append(QStringLiteral("\n"
- "uniform sampler3D ove_ociolut;\n"
- "\n"
- "vec4 %2(vec4 col) {\n"
- " return %1\n"
- "}\n").arg(shader_call, process_function_name));
-
-
- // Get pipeline-based shader to inject OCIO shader into
- OpenGLShaderPtr shader = OpenGLShader::CreateDefault(process_function_name, shader_text);
-
- // Release LUT
- xf->glBindTexture(GL_TEXTURE_3D, 0);
-
- xf->glActiveTexture(GL_TEXTURE0);
-
- return shader;
-}
-
-QString OpenGLShader::CodeDefaultFragment(QString function_name, const QString &shader_code)
-{
- // Create shader header
- QString frag_code = QStringLiteral("#version 150\n"
- "\n"
- "#ifdef GL_ES\n"
- "precision highp int;\n"
- "precision highp float;\n"
- "#endif\n"
- "\n"
- "uniform sampler2D ove_maintex;\n"
- "uniform vec2 ove_resolution;\n"
- "uniform bool ove_deinterlace;\n"
- "\n"
- "in vec2 ove_texcoord;\n"
- "\n"
- "out vec4 fragColor;\n"
- "\n");
-
- // Check if additional code was passed to this function, add it here
- if (!function_name.isEmpty() && !shader_code.isEmpty()) {
-
- // 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 `ove_texcoord`.
-
- frag_code.append(shader_code);
-
- } else {
-
- // No function to call
- function_name = QString();
-
- }
-
- // Our function_name arg will either resolve to the function added to this or to nothing, in
- // which case they'll just be benign brackets.
- frag_code.append(QStringLiteral("\n"
- "void main() {\n"
- " vec2 using_texcoord = ove_texcoord;\n"
- " if (ove_deinterlace) {\n"
- " // A very basic deinterlace that halves the vertical\n"
- " // resolution and linearly interpolates the two fields\n"
- " // by reading the texture coord between them.\n"
- " float half_vert = round(ove_resolution.y / 2.0);\n"
- " using_texcoord.y = (round(using_texcoord.y * half_vert) + 0.25) / half_vert;\n"
- " }\n"
- " vec4 color = %1(texture(ove_maintex, using_texcoord));\n"
- " fragColor = color;\n"
- "}\n").arg(function_name));
-
- return frag_code;
-}
-
-QString OpenGLShader::CodeDefaultVertex()
-{
- // Generate vertex shader
- return QStringLiteral("#version 150\n"
- "\n"
- "#ifdef GL_ES\n"
- "precision highp int;\n"
- "precision highp float;\n"
- "#endif\n"
- "\n"
- "uniform mat4 ove_mvpmat;\n"
- "\n"
- "in vec4 a_position;\n"
- "in vec2 a_texcoord;\n"
- "\n"
- "out vec2 ove_texcoord;\n"
- "\n"
- "void main() {\n"
- " gl_Position = ove_mvpmat * a_position;\n"
- " ove_texcoord = a_texcoord;\n"
- "}\n");
-}
-
-QString OpenGLShader::CodeAlphaDisassociate(const QString &function_name)
-{
- return 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 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 QStringLiteral("vec4 %1(vec4 col) {\n"
- " return vec4(col.rgb * col.a, col.a);\n"
- "}\n").arg(function_name);
-}
-
-OLIVE_NAMESPACE_EXIT
diff --git a/app/render/backend/opengl/openglshader.h b/app/render/backend/opengl/openglshader.h
deleted file mode 100644
index 452dc3c3d..000000000
--- a/app/render/backend/opengl/openglshader.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 OPENGLSHADER_H
-#define OPENGLSHADER_H
-
-#include
-#include
-
-#include
-namespace OCIO = OCIO_NAMESPACE::v1;
-
-#include "common/define.h"
-
-OLIVE_NAMESPACE_ENTER
-
-class OpenGLShader;
-using OpenGLShaderPtr = std::shared_ptr;
-
-/**
- * @brief A simple QOpenGLShaderProgram derivative with static functions for creating
- */
-class OpenGLShader : public QOpenGLShaderProgram {
-public:
- OpenGLShader() = default;
-
- static OpenGLShaderPtr Create();
-
- static OpenGLShaderPtr CreateDefault(const QString &function_name = QString(),
- const QString &shader_code = QString());
-
- static OpenGLShaderPtr CreateOCIO(QOpenGLContext* ctx,
- GLuint& lut_texture,
- OCIO::ConstProcessorRcPtr processor,
- bool alpha_is_associated);
-
- static QString CodeDefaultFragment(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);
-
-};
-
-OLIVE_NAMESPACE_EXIT
-
-#endif // OPENGLSHADER_H
diff --git a/app/render/backend/opengl/opengltexture.cpp b/app/render/backend/opengl/opengltexture.cpp
deleted file mode 100644
index 9954e863c..000000000
--- a/app/render/backend/opengl/opengltexture.cpp
+++ /dev/null
@@ -1,191 +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 "opengltexture.h"
-
-#include
-#include
-#include
-
-#include "openglrenderfunctions.h"
-#include "render/pixelformat.h"
-
-OLIVE_NAMESPACE_ENTER
-
-OpenGLTexture::OpenGLTexture() :
- created_ctx_(nullptr),
- texture_(0)
-{
-}
-
-OpenGLTexture::~OpenGLTexture()
-{
- Destroy();
-}
-
-bool OpenGLTexture::IsCreated() const
-{
- return (texture_);
-}
-
-void OpenGLTexture::Create(QOpenGLContext *ctx, const VideoParams ¶ms, const void* data, int linesize)
-{
- if (!ctx) {
- qWarning() << "OpenGLTexture::Create was passed an invalid context";
- return;
- }
-
- Destroy();
-
- created_ctx_ = ctx;
- params_ = params;
-
- connect(created_ctx_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Destroy()), Qt::DirectConnection);
-
- // Create main texture
- CreateInternal(created_ctx_, &texture_, data, linesize);
-}
-
-void OpenGLTexture::Create(QOpenGLContext *ctx, const VideoParams ¶ms)
-{
- Create(ctx, params, nullptr, 0);
-}
-
-void OpenGLTexture::Create(QOpenGLContext *ctx, FramePtr frame)
-{
- Create(ctx, frame.get());
-}
-
-void OpenGLTexture::Create(QOpenGLContext *ctx, Frame *frame)
-{
- Create(ctx, frame->video_params(), frame->data(), frame->linesize_pixels());
-}
-
-void OpenGLTexture::Destroy()
-{
- if (created_ctx_) {
- disconnect(created_ctx_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Destroy()));
-
- created_ctx_->functions()->glDeleteTextures(1, &texture_);
- texture_ = 0;
-
- created_ctx_ = nullptr;
- }
-}
-
-void OpenGLTexture::Bind()
-{
- created_ctx_->functions()->glBindTexture(GL_TEXTURE_2D, texture_);
-}
-
-void OpenGLTexture::Release()
-{
- created_ctx_->functions()->glBindTexture(GL_TEXTURE_2D, 0);
-}
-
-void OpenGLTexture::SetPixelAspectRatio(const rational &r)
-{
- params_ = VideoParams(params_.width(),
- params_.height(),
- params_.time_base(),
- params_.format(),
- r,
- params_.interlacing(),
- params_.divider());
-}
-
-void OpenGLTexture::Upload(FramePtr frame)
-{
- Upload(frame.get());
-}
-
-void OpenGLTexture::Upload(Frame *frame)
-{
- Upload(frame->data(), frame->linesize_pixels());
-}
-
-void OpenGLTexture::Upload(const void *data, int linesize)
-{
- if (!IsCreated()) {
- qWarning() << "OpenGLTexture::Upload() called while it wasn't created";
- return;
- }
-
- Bind();
-
- created_ctx_->functions()->glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize);
-
- created_ctx_->functions()->glTexSubImage2D(GL_TEXTURE_2D,
- 0,
- 0,
- 0,
- width(),
- height(),
- OpenGLRenderFunctions::GetPixelFormat(format()),
- OpenGLRenderFunctions::GetPixelType(format()),
- data);
-
- created_ctx_->functions()->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
-
- Release();
-}
-
-void OpenGLTexture::CreateInternal(QOpenGLContext* create_ctx, GLuint* tex, const void *data, int linesize)
-{
- QOpenGLFunctions* f = create_ctx->functions();
-
- // Create texture
- f->glGenTextures(1, tex);
-
- // Verify texture
- if (texture_ == 0) {
- qWarning() << "OpenGL texture creation failed";
- return;
- }
-
- // Bind texture
- f->glBindTexture(GL_TEXTURE_2D, *tex);
-
- // Set linesize
- f->glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize);
-
- // Allocate storage for texture
- f->glTexImage2D(GL_TEXTURE_2D,
- 0,
- OpenGLRenderFunctions::GetInternalFormat(format()),
- width(),
- height(),
- 0,
- OpenGLRenderFunctions::GetPixelFormat(format()),
- OpenGLRenderFunctions::GetPixelType(format()),
- data);
-
- // Return linesize to default
- f->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
-
- // Set texture filtering to bilinear
- f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
- f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
- // Release texture
- f->glBindTexture(GL_TEXTURE_2D, 0);
-}
-
-OLIVE_NAMESPACE_EXIT
diff --git a/app/render/backend/opengl/opengltexture.h b/app/render/backend/opengl/opengltexture.h
deleted file mode 100644
index 301383da9..000000000
--- a/app/render/backend/opengl/opengltexture.h
+++ /dev/null
@@ -1,118 +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 OPENGLTEXTURE_H
-#define OPENGLTEXTURE_H
-
-#include
-#include
-
-#include "codec/frame.h"
-#include "render/pixelformat.h"
-
-OLIVE_NAMESPACE_ENTER
-
-/**
- * @brief A class wrapper around an OpenGL texture
- */
-class OpenGLTexture : public QObject
-{
- Q_OBJECT
-public:
- OpenGLTexture();
- virtual ~OpenGLTexture() override;
-
- DISABLE_COPY_MOVE(OpenGLTexture)
-
- void Create(QOpenGLContext* ctx, const VideoParams& params, const void *data, int linesize);
- void Create(QOpenGLContext* ctx, const VideoParams& params);
- void Create(QOpenGLContext* ctx, FramePtr frame);
- void Create(QOpenGLContext* ctx, Frame* frame);
-
- bool IsCreated() const;
-
- void Bind();
-
- void Release();
-
- const VideoParams& params() const
- {
- return params_;
- }
-
- const int& width() const
- {
- return params_.effective_width();
- }
-
- const int& height() const
- {
- return params_.effective_height();
- }
-
- const PixelFormat::Format &format() const
- {
- return params_.format();
- }
-
- const GLuint& texture() const
- {
- return texture_;
- }
-
- const int& divider() const
- {
- return params_.divider();
- }
-
- /**
- * @brief Changes the pixel aspect ratio metadata of this textuer
- *
- * This metadata is important for our render pipeline, but we don't need to do any re-allocation
- * to set it like we do with other VideoParam changes, so we provide a function to change only
- * the PAR here.
- */
- void SetPixelAspectRatio(const rational& r);
-
- void Upload(FramePtr frame);
- void Upload(Frame* frame);
- void Upload(const void *data, int linesize);
-
-public slots:
- void Destroy();
-
-private:
- void CreateInternal(QOpenGLContext *create_ctx, GLuint *tex, const void *data, int linesize);
-
- QOpenGLContext* created_ctx_;
-
- GLuint texture_;
-
- VideoParams params_;
-
-};
-
-using OpenGLTexturePtr = std::shared_ptr;
-
-OLIVE_NAMESPACE_EXIT
-
-Q_DECLARE_METATYPE(OLIVE_NAMESPACE::OpenGLTexturePtr)
-
-#endif // OPENGLTEXTURE_H
diff --git a/app/render/backend/opengl/opengltexturecache.cpp b/app/render/backend/opengl/opengltexturecache.cpp
deleted file mode 100644
index e00fc8f42..000000000
--- a/app/render/backend/opengl/opengltexturecache.cpp
+++ /dev/null
@@ -1,121 +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 "opengltexturecache.h"
-
-OLIVE_NAMESPACE_ENTER
-
-OpenGLTextureCache::~OpenGLTextureCache()
-{
- foreach (Reference* ref, existing_references_) {
- ref->ParentKilled();
- }
-}
-
-OpenGLTextureCache::ReferencePtr OpenGLTextureCache::Get(QOpenGLContext *ctx, FramePtr frame)
-{
- return Get(ctx, frame.get());
-}
-
-OpenGLTextureCache::ReferencePtr OpenGLTextureCache::Get(QOpenGLContext *ctx, Frame *frame)
-{
- return Get(ctx, frame->video_params(), frame->data(), frame->linesize_pixels());
-}
-
-OpenGLTextureCache::ReferencePtr OpenGLTextureCache::Get(QOpenGLContext* ctx, const VideoParams ¶ms, const void *data, int linesize)
-{
- OpenGLTexturePtr texture = nullptr;
-
- lock_.lock();
-
- // Iterate through textures and see if we have one that matches these parameters
- for (int i=0;iwidth() == params.effective_width()
- && test->height() == params.effective_height()
- && test->format() == params.format()) {
- texture = test;
- available_textures_.removeAt(i);
- break;
- }
- }
-
- // If we didn't find a texture, we'll need to create one
- if (!texture) {
- texture = std::make_shared();
- texture->Create(ctx, params);
- }
-
- texture->SetPixelAspectRatio(params.pixel_aspect_ratio());
-
- ReferencePtr ref = std::make_shared(this, texture);
- existing_references_.append(ref.get());
-
- lock_.unlock();
-
- if (data) {
- texture->Upload(data, linesize);
- }
-
- return ref;
-}
-
-OpenGLTextureCache::ReferencePtr OpenGLTextureCache::Get(QOpenGLContext *ctx, const VideoParams ¶ms)
-{
- return Get(ctx, params, nullptr, 0);
-}
-
-void OpenGLTextureCache::Relinquish(OpenGLTextureCache::Reference *ref)
-{
- OpenGLTexturePtr tex = ref->texture();
-
- lock_.lock();
-
- existing_references_.removeOne(ref);
- available_textures_.append(tex);
-
- lock_.unlock();
-}
-
-OpenGLTextureCache::Reference::Reference(OpenGLTextureCache *parent, OpenGLTexturePtr texture) :
- parent_(parent),
- texture_(texture)
-{
-}
-
-OpenGLTextureCache::Reference::~Reference()
-{
- if (parent_) {
- parent_->Relinquish(this);
- }
-}
-
-OpenGLTexturePtr OpenGLTextureCache::Reference::texture()
-{
- return texture_;
-}
-
-void OpenGLTextureCache::Reference::ParentKilled()
-{
- parent_ = nullptr;
-}
-
-OLIVE_NAMESPACE_EXIT
diff --git a/app/render/backend/opengl/opengltexturecache.h b/app/render/backend/opengl/opengltexturecache.h
deleted file mode 100644
index 276cdd150..000000000
--- a/app/render/backend/opengl/opengltexturecache.h
+++ /dev/null
@@ -1,80 +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 OPENGLTEXTURECACHE_H
-#define OPENGLTEXTURECACHE_H
-
-#include
-
-#include "openglframebuffer.h"
-#include "opengltexture.h"
-#include "render/videoparams.h"
-
-OLIVE_NAMESPACE_ENTER
-
-class OpenGLTextureCache
-{
-public:
- class Reference {
- public:
- Reference(OpenGLTextureCache* parent, OpenGLTexturePtr texture);
- ~Reference();
-
- DISABLE_COPY_MOVE(Reference)
-
- OpenGLTexturePtr texture();
-
- void ParentKilled();
-
- private:
- OpenGLTextureCache* parent_;
-
- OpenGLTexturePtr texture_;
- };
-
- using ReferencePtr = std::shared_ptr;
-
- OpenGLTextureCache() = default;
-
- ~OpenGLTextureCache();
-
- DISABLE_COPY_MOVE(OpenGLTextureCache)
-
- ReferencePtr Get(QOpenGLContext *ctx, FramePtr frame);
- ReferencePtr Get(QOpenGLContext *ctx, Frame* frame);
- ReferencePtr Get(QOpenGLContext *ctx, const VideoParams& params, const void *data, int linesize);
- ReferencePtr Get(QOpenGLContext *ctx, const VideoParams& params);
-
-private:
- void Relinquish(Reference* ref);
-
- QMutex lock_;
-
- QList available_textures_;
-
- QList existing_references_;
-
-};
-
-OLIVE_NAMESPACE_EXIT
-
-Q_DECLARE_METATYPE(OLIVE_NAMESPACE::OpenGLTextureCache::ReferencePtr)
-
-#endif // OPENGLTEXTURECACHE_H
diff --git a/app/render/backend/rendercontext.cpp b/app/render/backend/rendercontext.cpp
index a9a4ce73e..2f07702a9 100644
--- a/app/render/backend/rendercontext.cpp
+++ b/app/render/backend/rendercontext.cpp
@@ -1,6 +1,36 @@
+/***
+
+ 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 "rendercontext.h"
-RenderContext::RenderContext()
+OLIVE_NAMESPACE_ENTER
+
+RenderContext::RenderContext(QObject *parent) :
+ QObject(parent)
{
}
+
+QVariant RenderContext::CreateTexture(const VideoParams ¶m)
+{
+ return CreateTexture(param, nullptr, 0);
+}
+
+OLIVE_NAMESPACE_EXIT
diff --git a/app/render/backend/rendercontext.h b/app/render/backend/rendercontext.h
index fc443507e..dba4a9e51 100644
--- a/app/render/backend/rendercontext.h
+++ b/app/render/backend/rendercontext.h
@@ -1,11 +1,65 @@
+/***
+
+ 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 RENDERCONTEXT_H
#define RENDERCONTEXT_H
+#include
+#include
-class RenderContext
+#include "common/define.h"
+#include "render/videoparams.h"
+
+OLIVE_NAMESPACE_ENTER
+
+class RenderContext : public QObject
{
+ Q_OBJECT
public:
- RenderContext();
+ RenderContext(QObject* parent = nullptr);
+
+ virtual ~RenderContext() override;
+
+ virtual bool Init() = 0;
+
+public slots:
+ virtual void PostInit() = 0;
+
+ virtual void Destroy() = 0;
+
+ virtual QVariant CreateTexture(const OLIVE_NAMESPACE::VideoParams& param, void* data, int linesize) = 0;
+
+ virtual void DestroyTexture(QVariant texture) = 0;
+
+ virtual void UploadToTexture(QVariant texture, void* data, int linesize) = 0;
+
+ virtual void DownloadFromTexture(QVariant texture, void* data, int linesize) = 0;
+
+ virtual QVariant CreateShader();
+
+ virtual VideoParams GetParamsFromTexture(QVariant texture) = 0;
+
+ QVariant CreateTexture(const OLIVE_NAMESPACE::VideoParams& param);
+
};
+OLIVE_NAMESPACE_EXIT
+
#endif // RENDERCONTEXT_H
diff --git a/app/render/backend/rendercontextthreadwrapper.cpp b/app/render/backend/rendercontextthreadwrapper.cpp
new file mode 100644
index 000000000..397189809
--- /dev/null
+++ b/app/render/backend/rendercontextthreadwrapper.cpp
@@ -0,0 +1,107 @@
+/***
+
+ 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 "rendercontextthreadwrapper.h"
+
+OLIVE_NAMESPACE_ENTER
+
+RenderContextThreadWrapper::RenderContextThreadWrapper(RenderContext *inner, QObject *parent) :
+ RenderContext(parent),
+ inner_(inner),
+ thread_(nullptr)
+{
+ inner_->setParent(this);
+}
+
+bool RenderContextThreadWrapper::Init()
+{
+ // Create thread
+ QThread* thread = new QThread(this);
+ thread->start(QThread::IdlePriority);
+
+ // Move context to thread
+ inner_->moveToThread(thread);
+
+ // Init context in main thread
+ inner_->Init();
+
+ // Queue post-init in new thread
+ QMetaObject::invokeMethod(inner_, "PostInit", Qt::QueuedConnection);
+}
+
+void RenderContextThreadWrapper::Destroy()
+{
+ if (thread_) {
+ QMetaObject::invokeMethod(inner_, "Destroy", Qt::QueuedConnection);
+
+ thread_->quit();
+ thread_->wait();
+ delete thread_;
+ thread_ = nullptr;
+ }
+}
+
+QVariant RenderContextThreadWrapper::CreateTexture(const VideoParams ¶m, void *data, int linesize)
+{
+ QVariant v;
+
+ QMetaObject::invokeMethod(inner_, "CreateTexture", Qt::BlockingQueuedConnection,
+ Q_RETURN_ARG(QVariant, v),
+ OLIVE_NS_CONST_ARG(VideoParams&, param),
+ Q_ARG(void*, data),
+ Q_ARG(int, linesize));
+
+ return v;
+}
+
+void RenderContextThreadWrapper::DestroyTexture(QVariant texture)
+{
+ QMetaObject::invokeMethod(inner_, "DestroyTexture", Qt::QueuedConnection,
+ Q_ARG(QVariant, texture));
+}
+
+void RenderContextThreadWrapper::UploadToTexture(QVariant texture, void *data, int linesize)
+{
+ QMetaObject::invokeMethod(inner_, "UploadToTexture", Qt::QueuedConnection,
+ Q_ARG(QVariant, texture),
+ Q_ARG(void*, data),
+ Q_ARG(int, linesize));
+}
+
+void RenderContextThreadWrapper::DownloadFromTexture(QVariant texture, void *data, int linesize)
+{
+ QMetaObject::invokeMethod(inner_, "DownloadFromTexture", Qt::QueuedConnection,
+ Q_ARG(QVariant, texture),
+ Q_ARG(void*, data),
+ Q_ARG(int, linesize));
+}
+
+VideoParams RenderContextThreadWrapper::GetParamsFromTexture(QVariant texture)
+{
+ VideoParams p;
+
+ QMetaObject::invokeMethod(inner_, "GetParamsFromTexture", Qt::BlockingQueuedConnection,
+ Q_RETURN_ARG(VideoParams, p),
+ Q_ARG(QVariant, texture));
+
+ return p;
+}
+
+OLIVE_NAMESPACE_EXIT
diff --git a/app/render/backend/rendercontextthreadwrapper.h b/app/render/backend/rendercontextthreadwrapper.h
new file mode 100644
index 000000000..1a8a7f6c1
--- /dev/null
+++ b/app/render/backend/rendercontextthreadwrapper.h
@@ -0,0 +1,66 @@
+/***
+
+ 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 RENDERCONTEXTTHREADWRAPPER_H
+#define RENDERCONTEXTTHREADWRAPPER_H
+
+#include
+
+#include "rendercontext.h"
+
+OLIVE_NAMESPACE_ENTER
+
+class RenderContextThreadWrapper : public RenderContext
+{
+public:
+ RenderContextThreadWrapper(RenderContext* inner, QObject* parent = nullptr);
+
+ virtual ~RenderContextThreadWrapper() override
+ {
+ Destroy();
+ }
+
+ virtual bool Init() override;
+
+public slots:
+ virtual void PostInit() override{}
+
+ virtual void Destroy() override;
+
+ virtual QVariant CreateTexture(const VideoParams& param, void* data, int linesize) override;
+
+ virtual void DestroyTexture(QVariant texture) override;
+
+ virtual void UploadToTexture(QVariant texture, void* data, int linesize) override;
+
+ virtual void DownloadFromTexture(QVariant texture, void* data, int linesize) override;
+
+ virtual VideoParams GetParamsFromTexture(QVariant texture) override;
+
+private:
+ RenderContext* inner_;
+
+ QThread* thread_;
+
+};
+
+OLIVE_NAMESPACE_EXIT
+
+#endif // RENDERCONTEXTTHREADWRAPPER_H
diff --git a/app/render/backend/renderframebuffer.cpp b/app/render/backend/renderframebuffer.cpp
deleted file mode 100644
index dd03c9d59..000000000
--- a/app/render/backend/renderframebuffer.cpp
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "renderframebuffer.h"
-
-RenderFrameBuffer::RenderFrameBuffer()
-{
-
-}
diff --git a/app/render/backend/renderframebuffer.h b/app/render/backend/renderframebuffer.h
deleted file mode 100644
index b4b928826..000000000
--- a/app/render/backend/renderframebuffer.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef RENDERFRAMEBUFFER_H
-#define RENDERFRAMEBUFFER_H
-
-
-class RenderFrameBuffer
-{
-public:
- RenderFrameBuffer();
-};
-
-#endif // RENDERFRAMEBUFFER_H
diff --git a/app/render/backend/rendershader.cpp b/app/render/backend/rendershader.cpp
deleted file mode 100644
index a90f3a1e3..000000000
--- a/app/render/backend/rendershader.cpp
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "rendershader.h"
-
-RenderShader::RenderShader()
-{
-
-}
diff --git a/app/render/backend/rendershader.h b/app/render/backend/rendershader.h
deleted file mode 100644
index 63f5f97de..000000000
--- a/app/render/backend/rendershader.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef RENDERSHADER_H
-#define RENDERSHADER_H
-
-
-class RenderShader
-{
-public:
- RenderShader();
-};
-
-#endif // RENDERSHADER_H
diff --git a/app/render/backend/rendertexture.cpp b/app/render/backend/rendertexture.cpp
deleted file mode 100644
index 0ba906451..000000000
--- a/app/render/backend/rendertexture.cpp
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "rendertexture.h"
-
-RenderTexture::RenderTexture(RenderContext *ctx)
-{
-
-}
diff --git a/app/render/backend/rendertexture.h b/app/render/backend/rendertexture.h
deleted file mode 100644
index 462ad26b9..000000000
--- a/app/render/backend/rendertexture.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef RENDERTEXTURE_H
-#define RENDERTEXTURE_H
-
-#include "rendercontext.h"
-
-class RenderTexture
-{
-public:
- RenderTexture(RenderContext* ctx);
-};
-
-#endif // RENDERTEXTURE_H
diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp
index 8e31da39e..b0945e611 100644
--- a/app/render/previewautocacher.cpp
+++ b/app/render/previewautocacher.cpp
@@ -4,6 +4,7 @@
#include
#include "render/rendermanager.h"
+#include "render/renderprocessor.h"
OLIVE_NAMESPACE_ENTER
@@ -177,6 +178,35 @@ void PreviewAutoCacher::AudioRendered()
viewer_node_->audio_playback_cache()->WritePCM(audio_tasks_.value(watcher),
watcher->Get().value(),
watcher->GetTicket()->GetJobTime());
+
+ // Retrieve visual waveforms
+ QVector waveform_list = watcher->GetTicket()->property("waveforms").value< QVector >();
+ foreach (const RenderProcessor::RenderedWaveform& waveform_info, waveform_list) {
+ // Find original track
+ TrackOutput* track = nullptr;
+
+ for (auto it=copy_map_.cbegin(); it!=copy_map_.cend(); it++) {
+ if (it.value() == waveform_info.track) {
+ track = static_cast(it.key());
+ break;
+ }
+ }
+
+ if (track) {
+ QList valid_ranges = viewer_node_->audio_playback_cache()->GetValidRanges(waveform_info.range,
+ watcher->GetTicket()->GetJobTime());
+ if (!valid_ranges.isEmpty()) {
+ // Generate visual waveform in this background thread
+ track->waveform().set_channel_count(viewer_node_->audio_params().channel_count());
+
+ foreach (const TimeRange& r, valid_ranges) {
+ track->waveform().OverwriteSums(waveform_info.waveform, r.in(), r.in() - waveform_info.range.in(), r.length());
+ }
+
+ emit track->PreviewChanged();
+ }
+ }
+ }
}
audio_tasks_.remove(watcher);
diff --git a/app/render/rendermanager.cpp b/app/render/rendermanager.cpp
index 3c0ff4e26..546b90784 100644
--- a/app/render/rendermanager.cpp
+++ b/app/render/rendermanager.cpp
@@ -22,11 +22,14 @@
#include
#include
+#include
#include
#include "config/config.h"
#include "core.h"
-#include "render/backend/opengl/openglproxy.h"
+#include "render/backend/opengl/openglcontext.h"
+#include "render/backend/rendercontextthreadwrapper.h"
+#include "renderprocessor.h"
#include "task/conform/conform.h"
#include "task/taskmanager.h"
#include "window/mainwindow/mainwindow.h"
@@ -38,13 +41,8 @@ RenderManager* RenderManager::instance_ = nullptr;
RenderManager::RenderManager(QObject *parent) :
ThreadPool(QThread::IdlePriority, 0, parent)
{
- // Initialize OpenGL service
- OpenGLProxy::CreateInstance();
-}
-
-RenderManager::~RenderManager()
-{
- OpenGLProxy::DestroyInstance();
+ context_ = new RenderContextThreadWrapper(new OpenGLContext(), this);
+ context_->Init();
}
QByteArray RenderManager::Hash(const Node *n, const VideoParams ¶ms, const rational &time)
@@ -63,13 +61,23 @@ QByteArray RenderManager::Hash(const Node *n, const VideoParams ¶ms, const r
return hasher.result();
}
-RenderTicketPtr RenderManager::RenderFrame(ViewerOutput* viewer, const rational &time, RenderMode::Mode mode, bool prioritize)
+RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, const rational &time, RenderMode::Mode mode, bool prioritize)
+{
+ return RenderFrame(viewer, time, mode,
+ QSize(),
+ QMatrix4x4(),
+ prioritize);
+}
+
+RenderTicketPtr RenderManager::RenderFrame(ViewerOutput* viewer, const rational &time, RenderMode::Mode mode, const QSize &force_size, const QMatrix4x4 &matrix, bool prioritize)
{
// Create ticket
RenderTicketPtr ticket = std::make_shared();
ticket->setProperty("viewer", Node::PtrToValue(viewer));
ticket->setProperty("time", QVariant::fromValue(time));
+ ticket->setProperty("size", force_size);
+ ticket->setProperty("matrix", matrix);
ticket->setProperty("mode", mode);
ticket->setProperty("type", kTypeVideo);
@@ -81,7 +89,7 @@ RenderTicketPtr RenderManager::RenderFrame(ViewerOutput* viewer, const rational
return ticket;
}
-RenderTicketPtr RenderManager::RenderAudio(ViewerOutput* viewer, const TimeRange &r, bool prioritize)
+RenderTicketPtr RenderManager::RenderAudio(ViewerOutput* viewer, const TimeRange &r, bool generate_waveforms, bool prioritize)
{
// Create ticket
RenderTicketPtr ticket = std::make_shared();
@@ -89,6 +97,7 @@ RenderTicketPtr RenderManager::RenderAudio(ViewerOutput* viewer, const TimeRange
ticket->setProperty("viewer", Node::PtrToValue(viewer));
ticket->setProperty("time", QVariant::fromValue(r));
ticket->setProperty("type", kTypeAudio);
+ ticket->setProperty("waveforms", generate_waveforms);
// Queue appending the ticket and running the next job on our thread to make this function thread-safe
QMetaObject::invokeMethod(this, "AddTicket", Qt::AutoConnection,
@@ -118,84 +127,7 @@ RenderTicketPtr RenderManager::SaveFrameToCache(FrameHashCache *cache, FramePtr
void RenderManager::RunTicket(RenderTicketPtr ticket) const
{
- // Depending on the render ticket type, start a job
- TicketType type = ticket->property("type").value();
-
- switch (type) {
- case kTypeVideo:
- RenderFrameInternal(ticket);
- break;
- case kTypeAudio:
- RenderAudioInternal(ticket);
- break;
- case kTypeVideoDownload:
- SaveFrameToCacheInternal(ticket);
- break;
- default:
- // Fail
- ticket->Cancel();
- }
-}
-
-void RenderManager::RenderFrameInternal(RenderTicketPtr ticket)
-{
- ViewerOutput* viewer = Node::ValueToPtr(ticket->property("viewer"));
- rational time = ticket->property("time").value();
-
- ticket->Start();
-
- qDebug() << "STUB: Rendered" << time << "frames for" << viewer;
-
- FramePtr frame = Frame::Create();
- frame->set_video_params(viewer->video_params());
- frame->allocate();
-
- ticket->Finish(QVariant::fromValue(frame), false);
-}
-
-void RenderManager::RenderAudioInternal(RenderTicketPtr ticket)
-{
- ViewerOutput* viewer = Node::ValueToPtr(ticket->property("viewer"));
- TimeRange time = ticket->property("time").value();
-
- ticket->Start();
-
- qDebug() << "STUB: Rendered" << time << "audio for" << viewer;
-
- ticket->Finish(QVariant::fromValue(SampleBuffer::CreateAllocated(viewer->audio_params(), time.length())), false);
-}
-
-void RenderManager::SaveFrameToCacheInternal(RenderTicketPtr ticket)
-{
- FrameHashCache* cache = Node::ValueToPtr(ticket->property("cache"));
- FramePtr frame = ticket->property("frame").value();
- QByteArray hash = ticket->property("hash").toByteArray();
-
- ticket->Start();
-
- ticket->Finish(cache->SaveCacheFrame(hash, frame), false);
-}
-
-void RenderManager::WorkerGeneratedWaveform(RenderTicketPtr ticket, TrackOutput *track, AudioVisualWaveform samples, TimeRange range)
-{
- ViewerOutput* viewer = Node::ValueToPtr(ticket->property("viewer"));
-
- QList valid_ranges = viewer->audio_playback_cache()->GetValidRanges(range,
- ticket->GetJobTime());
- if (!valid_ranges.isEmpty()) {
- // Generate visual waveform in this background thread
- track->waveform_lock()->lock();
-
- track->waveform().set_channel_count(viewer->audio_params().channel_count());
-
- foreach (const TimeRange& r, valid_ranges) {
- track->waveform().OverwriteSums(samples, r.in(), r.in() - range.in(), r.length());
- }
-
- track->waveform_lock()->unlock();
-
- emit track->PreviewChanged();
- }
+ RenderProcessor::Process(ticket, context_);
}
OLIVE_NAMESPACE_EXIT
diff --git a/app/render/rendermanager.h b/app/render/rendermanager.h
index 035bf7938..68e26905b 100644
--- a/app/render/rendermanager.h
+++ b/app/render/rendermanager.h
@@ -29,6 +29,8 @@
#include "decodercache.h"
#include "node/graph.h"
#include "node/output/viewer/viewer.h"
+#include "node/traverser.h"
+#include "render/backend/rendercontext.h"
#include "threading/threadpool.h"
OLIVE_NAMESPACE_ENTER
@@ -78,6 +80,7 @@ public:
* This function is thread-safe.
*/
RenderTicketPtr RenderFrame(ViewerOutput* viewer, const rational& time, RenderMode::Mode mode, bool prioritize = false);
+ RenderTicketPtr RenderFrame(ViewerOutput* viewer, const rational& time, RenderMode::Mode mode, const QSize& force_size, const QMatrix4x4& matrix, bool prioritize = false);
/**
* @brief Asynchronously generate a chunk of audio
@@ -89,7 +92,7 @@ public:
*
* This function is thread-safe.
*/
- RenderTicketPtr RenderAudio(ViewerOutput* viewer, const TimeRange& r, bool prioritize = false);
+ RenderTicketPtr RenderAudio(ViewerOutput* viewer, const TimeRange& r, bool generate_waveforms, bool prioritize = false);
RenderTicketPtr SaveFrameToCache(FrameHashCache* cache, FramePtr frame, const QByteArray& hash, bool prioritize = false);
@@ -104,20 +107,11 @@ public:
signals:
private:
- static void RenderFrameInternal(RenderTicketPtr ticket);
-
- static void RenderAudioInternal(RenderTicketPtr ticket);
-
- static void SaveFrameToCacheInternal(RenderTicketPtr ticket);
-
RenderManager(QObject* parent = nullptr);
- virtual ~RenderManager() override;
-
static RenderManager* instance_;
-private slots:
- void WorkerGeneratedWaveform(OLIVE_NAMESPACE::RenderTicketPtr ticket, OLIVE_NAMESPACE::TrackOutput* track, OLIVE_NAMESPACE::AudioVisualWaveform samples, OLIVE_NAMESPACE::TimeRange range);
+ RenderContext* context_;
};
diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp
new file mode 100644
index 000000000..b3c354c05
--- /dev/null
+++ b/app/render/renderprocessor.cpp
@@ -0,0 +1,630 @@
+/***
+
+ 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 "renderprocessor.h"
+
+#include "rendermanager.h"
+
+OLIVE_NAMESPACE_ENTER
+
+RenderProcessor::RenderProcessor(RenderTicketPtr ticket, RenderContext *render_ctx) :
+ ticket_(ticket),
+ render_ctx_(render_ctx)
+{
+}
+
+void RenderProcessor::Run()
+{
+ // Depending on the render ticket type, start a job
+ RenderManager::TicketType type = ticket_->property("type").value();
+
+ ticket_->Start();
+
+ switch (type) {
+ case RenderManager::kTypeVideo:
+ {
+ ViewerOutput* viewer = Node::ValueToPtr(ticket_->property("viewer"));
+ rational time = ticket_->property("time").value();
+
+ NodeValueTable table = ProcessInput(viewer->texture_input(),
+ TimeRange(time, time + viewer->video_params().time_base()));
+
+ QVariant texture = table.Get(NodeParam::kTexture);
+
+ QSize frame_size = ticket_->property("size").value();
+ if (frame_size.isNull()) {
+ frame_size = QSize(viewer->video_params().effective_width(),
+ viewer->video_params().effective_height());
+ }
+
+ FramePtr frame = Frame::Create();
+ frame->set_timestamp(time);
+ frame->set_video_params(VideoParams(frame_size.width(),
+ frame_size.height(),
+ viewer->video_params().time_base(),
+ viewer->video_params().format(),
+ viewer->video_params().pixel_aspect_ratio(),
+ viewer->video_params().interlacing(),
+ viewer->video_params().divider()));
+ frame->allocate();
+
+ if (texture.isNull()) {
+ // Blank frame out
+ memset(frame->data(), 0, frame->allocated_size());
+ } else {
+ // Dump texture contents to frame
+ VideoParams tex_params = render_ctx_->GetParamsFromTexture(texture);
+
+ if (tex_params.width() != frame->width() || tex_params.height() != frame->height()) {
+ // FIXME: Blit this shit
+ }
+
+ render_ctx_->DownloadFromTexture(texture, frame->data(), frame->linesize_pixels());
+ }
+
+ ticket_->Finish(QVariant::fromValue(frame), IsCancelled());
+ break;
+ }
+ case RenderManager::kTypeAudio:
+ {
+ ViewerOutput* viewer = Node::ValueToPtr(ticket_->property("viewer"));
+ TimeRange time = ticket_->property("time").value();
+
+ NodeValueTable table = ProcessInput(viewer->samples_input(), time);
+
+ ticket_->Finish(table.Get(NodeParam::kSamples), IsCancelled());
+ break;
+ }
+ case RenderManager::kTypeVideoDownload:
+ {
+ FrameHashCache* cache = Node::ValueToPtr(ticket_->property("cache"));
+ FramePtr frame = ticket_->property("frame").value();
+ QByteArray hash = ticket_->property("hash").toByteArray();
+
+ ticket_->Finish(cache->SaveCacheFrame(hash, frame), false);
+ break;
+ }
+ default:
+ // Fail
+ ticket_->Cancel();
+ }
+
+ this->deleteLater();
+}
+
+void RenderProcessor::Process(RenderTicketPtr ticket, RenderContext *render_ctx)
+{
+ RenderProcessor p(ticket, render_ctx);
+ p.Run();
+}
+
+NodeValueTable RenderProcessor::GenerateBlockTable(const TrackOutput *track, const TimeRange &range)
+{
+ if (track->track_type() == Timeline::kTrackTypeAudio) {
+
+ const AudioParams& audio_params = Node::ValueToPtr(ticket_->property("viewer"))->audio_params();
+
+ QList active_blocks = track->BlocksAtTimeRange(range);
+
+ // All these blocks will need to output to a buffer so we create one here
+ SampleBufferPtr block_range_buffer = SampleBuffer::CreateAllocated(audio_params,
+ audio_params.time_to_samples(range.length()));
+ block_range_buffer->fill(0);
+
+ NodeValueTable merged_table;
+
+ // Loop through active blocks retrieving their audio
+ foreach (Block* b, active_blocks) {
+ TimeRange range_for_block(qMax(b->in(), range.in()),
+ qMin(b->out(), range.out()));
+
+ int destination_offset = audio_params.time_to_samples(range_for_block.in() - range.in());
+ int max_dest_sz = audio_params.time_to_samples(range_for_block.length());
+
+ // Destination buffer
+ NodeValueTable table = GenerateTable(b, range_for_block);
+ SampleBufferPtr samples_from_this_block = table.Take(NodeParam::kSamples).value();
+
+ if (!samples_from_this_block) {
+ // If we retrieved no samples from this block, do nothing
+ continue;
+ }
+
+ // FIXME: Doesn't handle reversing
+ if (b->speed_input()->is_keyframing() || b->speed_input()->is_connected()) {
+ // FIXME: We'll need to calculate the speed hoo boy
+ } else {
+ double speed_value = b->speed_input()->get_standard_value().toDouble();
+
+ if (qIsNull(speed_value)) {
+ // Just silence, don't think there's any other practical application of 0 speed audio
+ samples_from_this_block->fill(0);
+ } else if (!qFuzzyCompare(speed_value, 1.0)) {
+ // Multiply time
+ samples_from_this_block->speed(speed_value);
+ }
+ }
+
+ int copy_length = qMin(max_dest_sz, samples_from_this_block->sample_count());
+
+ // Copy samples into destination buffer
+ block_range_buffer->set(samples_from_this_block->const_data(), destination_offset, copy_length);
+
+ NodeValueTable::Merge({merged_table, table});
+ }
+
+ if (ticket_->property("waveforms").toBool()) {
+ // Generate a visual waveform and send it back to the main thread
+ AudioVisualWaveform visual_waveform;
+ visual_waveform.set_channel_count(audio_params.channel_count());
+ visual_waveform.OverwriteSamples(block_range_buffer, audio_params.sample_rate());
+
+ RenderedWaveform waveform_info = {track, visual_waveform, range};
+ QVector waveform_list = ticket_->property("waveforms").value< QVector >();
+ waveform_list.append(waveform_info);
+ ticket_->setProperty("waveforms", QVariant::fromValue(waveform_list));
+ }
+
+ merged_table.Push(NodeParam::kSamples, QVariant::fromValue(block_range_buffer), track);
+
+ return merged_table;
+
+ } else {
+ return NodeTraverser::GenerateBlockTable(track, range);
+ }
+}
+
+QVariant RenderProcessor::ProcessVideoFootage(StreamPtr stream, const rational &input_time)
+{
+ VideoStreamPtr video_stream = std::static_pointer_cast(stream);
+ rational time_match = (video_stream->video_type() == VideoStream::kVideoTypeStill) ? 0 : input_time;
+ QString colorspace_match = video_stream->get_colorspace_match_string();
+
+ QVariant value;
+ bool found_cache = false;
+
+ const VideoParams& video_params = Node::ValueToPtr(ticket_->property("viewer"))->video_params();
+
+ if (still_image_cache_.contains(stream.get())) {
+ const CachedStill& cs = still_image_cache_[stream.get()];
+
+ if (cs.colorspace == colorspace_match
+ && cs.alpha_is_associated == video_stream->premultiplied_alpha()
+ && cs.divider == video_params.divider()
+ && cs.time == time_match) {
+ value = cs.texture;
+ found_cache = true;
+ } else {
+ still_image_cache_.remove(stream.get());
+ }
+ }
+
+ if (!found_cache) {
+
+ DecoderPtr decoder = ResolveDecoderFromInput(stream);
+
+ if (decoder) {
+ FramePtr frame = decoder->RetrieveVideo(input_time,
+ video_params.divider());
+
+ if (frame) {
+ // Return a texture from the derived class
+ value = FootageFrameToTexture(stream, frame);
+
+ if (!value.isNull()) {
+ // Put this into the image cache instead
+ still_image_cache_.insert(stream.get(), {value,
+ colorspace_match,
+ video_stream->premultiplied_alpha(),
+ video_params .divider(),
+ time_match});
+ }
+ }
+ }
+
+ }
+
+ return value;
+}
+
+QVariant RenderProcessor::ProcessAudioFootage(StreamPtr stream, const TimeRange &input_time)
+{
+ QVariant value;
+
+ DecoderPtr decoder = ResolveDecoderFromInput(stream);
+
+ if (decoder) {
+ const AudioParams& audio_params = Node::ValueToPtr(ticket_->property("viewer"))->audio_params();
+
+ // See if we have a conformed version of this audio
+ if (!decoder->HasConformedVersion(audio_params)) {
+
+ // If not, the audio needs to be conformed
+ // For online rendering/export, it's a waste of time to render the audio until we have
+ // all we need, so we try to handle the conform ourselves
+ AudioStreamPtr as = std::static_pointer_cast(stream);
+
+ // Check if any other threads are conforming this audio
+ if (as->try_start_conforming(audio_params)) {
+
+ // If not, conform it ourselves
+ decoder->ConformAudio(&IsCancelled(), audio_params);
+
+ } else {
+
+ // If another thread is conforming already, hackily try to wait until it's done.
+ do {
+ QThread::msleep(1000);
+ } while (!as->has_conformed_version(audio_params) && !IsCancelled());
+
+ }
+
+ }
+
+ if (decoder->HasConformedVersion(audio_params)) {
+ SampleBufferPtr frame = decoder->RetrieveAudio(input_time.in(), input_time.length(),
+ audio_params);
+
+ if (frame) {
+ value = QVariant::fromValue(frame);
+ }
+ }
+ }
+
+ return value;
+}
+
+QVariant RenderProcessor::ProcessShader(const Node *node, const TimeRange &range, const ShaderJob &job)
+{
+ // If this node is iterative, we'll pick up which input here
+ GLuint iterative_input = 0;
+ QList textures_to_bind;
+ bool input_textures_have_alpha = false;
+
+ OpenGLShaderPtr shader = ResolveShaderFromCache(node, job.GetShaderID());
+
+ if (!shader) {
+ return QVariant();
+ }
+
+ shader->bind();
+
+ NodeValueMap::const_iterator it;
+ for (it=job.GetValues().constBegin(); it!=job.GetValues().constEnd(); it++) {
+ // See if the shader has takes this parameter as an input
+ int variable_location = shader->uniformLocation(it.key());
+
+ if (variable_location == -1) {
+ continue;
+ }
+
+ // See if this value corresponds to an input (NOTE: it may not and this may be null)
+ NodeInput* corresponding_input = node->GetInputWithID(it.key());
+
+ // This variable is used in the shader, let's set it
+ const QVariant& value = it.value().data();
+
+ NodeParam::DataType data_type = (it.value().type() != NodeParam::kNone)
+ ? it.value().type()
+ : corresponding_input->data_type();
+
+ switch (data_type) {
+ case NodeInput::kInt:
+ // kInt technically specifies a LongLong, but OpenGL doesn't support those. This may lead to
+ // over/underflows if the number is large enough, but the likelihood of that is quite low.
+ shader->setUniformValue(variable_location, value.toInt());
+ break;
+ case NodeInput::kFloat:
+ // kFloat technically specifies a double but as above, OpenGL doesn't support those.
+ shader->setUniformValue(variable_location, value.toFloat());
+ break;
+ case NodeInput::kVec2:
+ if (corresponding_input && corresponding_input->IsArray()) {
+ QVector nv = value.value< QVector >();
+ QVector a(nv.size());
+
+ for (int j=0;j();
+ }
+
+ shader->setUniformValueArray(variable_location, a.constData(), a.size());
+
+ int count_location = shader->uniformLocation(QStringLiteral("%1_count").arg(it.key()));
+ if (count_location > -1) {
+ shader->setUniformValue(count_location, a.size());
+ }
+ } else {
+ shader->setUniformValue(variable_location, value.value());
+ }
+ break;
+ case NodeInput::kVec3:
+ shader->setUniformValue(variable_location, value.value());
+ break;
+ case NodeInput::kVec4:
+ shader->setUniformValue(variable_location, value.value());
+ break;
+ case NodeInput::kMatrix:
+ shader->setUniformValue(variable_location, value.value());
+ break;
+ case NodeInput::kCombo:
+ shader->setUniformValue(variable_location, value.value());
+ break;
+ case NodeInput::kColor:
+ {
+ Color color = value.value();
+
+ shader->setUniformValue(variable_location, color.red(), color.green(), color.blue(), color.alpha());
+ break;
+ }
+ case NodeInput::kBoolean:
+ shader->setUniformValue(variable_location, value.toBool());
+ break;
+ case NodeInput::kBuffer:
+ case NodeInput::kTexture:
+ {
+ OpenGLTextureCache::ReferencePtr texture = value.value();
+
+ if (texture) {
+ if (PixelFormat::FormatHasAlphaChannel(texture->texture()->format())) {
+ input_textures_have_alpha = true;
+ }
+ }
+
+ // Set value to bound texture
+ shader->setUniformValue(variable_location, textures_to_bind.size());
+
+ // If this texture binding is the iterative input, set it here
+ if (corresponding_input && corresponding_input == job.GetIterativeInput()) {
+ iterative_input = textures_to_bind.size();
+ }
+
+ GLuint tex_id = texture ? texture->texture()->texture() : 0;
+ textures_to_bind.append(tex_id);
+
+ // Set enable flag if shader wants it
+ int enable_param_location = shader->uniformLocation(QStringLiteral("%1_enabled").arg(it.key()));
+ if (enable_param_location > -1) {
+ shader->setUniformValue(enable_param_location,
+ tex_id > 0);
+ }
+
+ if (tex_id > 0) {
+ // Set texture resolution if shader wants it
+ int res_param_location = shader->uniformLocation(QStringLiteral("%1_resolution").arg(it.key()));
+ if (res_param_location > -1) {
+ int adjusted_width = texture->texture()->width() * texture->texture()->divider();
+
+ // Adjust virtual width by pixel aspect if necessary
+ if (texture->texture()->params().pixel_aspect_ratio() != 1
+ || params.pixel_aspect_ratio() != 1) {
+ double relative_pixel_aspect = texture->texture()->params().pixel_aspect_ratio().toDouble() / params.pixel_aspect_ratio().toDouble();
+
+ adjusted_width = qRound(static_cast(adjusted_width) * relative_pixel_aspect);
+ }
+
+ shader->setUniformValue(res_param_location,
+ adjusted_width,
+ static_cast(texture->texture()->height() * texture->texture()->divider()));
+ }
+ }
+ break;
+ }
+ case NodeInput::kSamples:
+ case NodeInput::kText:
+ case NodeInput::kRational:
+ case NodeInput::kFont:
+ case NodeInput::kFile:
+ case NodeInput::kDecimal:
+ case NodeInput::kNumber:
+ case NodeInput::kString:
+ case NodeInput::kVector:
+ case NodeInput::kShaderJob:
+ case NodeInput::kSampleJob:
+ case NodeInput::kGenerateJob:
+ case NodeInput::kFootage:
+ case NodeInput::kNone:
+ case NodeInput::kAny:
+ break;
+ }
+ }
+
+ // Provide some standard args
+ shader->setUniformValue("ove_resolution",
+ static_cast(params.width()),
+ static_cast(params.height()));
+
+ shader->release();
+
+ // Create the output textures
+ PixelFormat::Format output_format = (input_textures_have_alpha || job.GetAlphaChannelRequired())
+ ? PixelFormat::GetFormatWithAlphaChannel(params.format())
+ : PixelFormat::GetFormatWithoutAlphaChannel(params.format());
+ VideoParams output_params(params.width(),
+ params.height(),
+ params.time_base(),
+ output_format,
+ params.pixel_aspect_ratio(),
+ params.interlacing(),
+ params.divider());
+
+ int real_iteration_count;
+ if (job.GetIterationCount() > 1 && job.GetIterativeInput()) {
+ real_iteration_count = job.GetIterationCount();
+ } else {
+ real_iteration_count = 1;
+ }
+
+ OpenGLTextureCache::ReferencePtr dst_refs[2];
+ dst_refs[0] = texture_cache_.Get(ctx_, output_params);
+
+ // If this node requires multiple iterations, get a texture for it too
+ if (real_iteration_count > 1) {
+ dst_refs[1] = texture_cache_.Get(ctx_, output_params);
+ }
+
+ // Some nodes use multiple iterations for optimization
+ OpenGLTextureCache::ReferencePtr input_tex, output_tex;
+
+ // Set up OpenGL parameters as necessary
+ functions_->glViewport(0, 0, params.effective_width(), params.effective_height());
+
+ // Bind all textures
+ for (int i=0; iglActiveTexture(GL_TEXTURE0 + i);
+ functions_->glBindTexture(GL_TEXTURE_2D, textures_to_bind.at(i));
+ OpenGLRenderFunctions::PrepareToDraw(functions_);
+ }
+
+ for (int iteration=0; iterationbind();
+ shader->setUniformValue("ove_iteration", iteration);
+ shader->release();
+
+ // Replace iterative input
+ if (iteration == 0) {
+ output_tex = dst_refs[0];
+ } else {
+ input_tex = dst_refs[(iteration+1)%2];
+ output_tex = dst_refs[iteration%2];
+
+ functions_->glActiveTexture(GL_TEXTURE0 + iterative_input);
+ functions_->glBindTexture(GL_TEXTURE_2D, input_tex->texture()->texture());
+ OpenGLRenderFunctions::PrepareToDraw(functions_);
+ }
+
+ buffer_.Attach(output_tex->texture(), true);
+ buffer_.Bind();
+
+ // Blit this texture through this shader
+ OpenGLRenderFunctions::Blit(shader);
+
+ buffer_.Release();
+ buffer_.Detach();
+ }
+
+ // Release any textures we bound before
+ for (int i=textures_to_bind.size()-1; i>=0; i--) {
+ functions_->glActiveTexture(GL_TEXTURE0 + i);
+ functions_->glBindTexture(GL_TEXTURE_2D, 0);
+ }
+
+ return QVariant::fromValue(output_tex);
+}
+
+QVariant RenderProcessor::ProcessSamples(const Node *node, const TimeRange &range, const SampleJob &job)
+{
+ if (!job.samples() || !job.samples()->is_allocated()) {
+ return QVariant();
+ }
+
+ SampleBufferPtr output_buffer = SampleBuffer::CreateAllocated(job.samples()->audio_params(), job.samples()->sample_count());
+ NodeValueDatabase value_db;
+
+ const AudioParams& audio_params = Node::ValueToPtr(ticket_->property("viewer"))->audio_params();
+
+ for (int i=0;isample_count();i++) {
+ // Calculate the exact rational time at this sample
+ double sample_to_second = static_cast(i) / static_cast(audio_params.sample_rate());
+
+ rational this_sample_time = rational::fromDouble(range.in().toDouble() + sample_to_second);
+
+ // Update all non-sample and non-footage inputs
+ NodeValueMap::const_iterator j;
+ for (j=job.GetValues().constBegin(); j!=job.GetValues().constEnd(); j++) {
+ NodeValueTable value;
+ NodeInput* corresponding_input = node->GetInputWithID(j.key());
+
+ if (corresponding_input) {
+ value = ProcessInput(corresponding_input, TimeRange(this_sample_time, this_sample_time));
+ } else {
+ value.Push(j.value());
+ }
+
+ value_db.Insert(j.key(), value);
+ }
+
+ AddGlobalsToDatabase(value_db, TimeRange(this_sample_time, this_sample_time));
+
+ node->ProcessSamples(value_db,
+ job.samples(),
+ output_buffer,
+ i);
+ }
+
+ return QVariant::fromValue(output_buffer);
+}
+
+QVariant RenderProcessor::ProcessFrameGeneration(const Node *node, const GenerateJob &job)
+{
+ FramePtr frame = Frame::Create();
+
+ const VideoParams& video_params = Node::ValueToPtr(ticket_->property("viewer"))->video_params();
+
+ PixelFormat::Format output_fmt;
+ if (job.GetAlphaChannelRequired()) {
+ output_fmt = PixelFormat::GetFormatWithAlphaChannel(video_params.format());
+ } else {
+ output_fmt = PixelFormat::GetFormatWithoutAlphaChannel(video_params.format());
+ }
+
+ frame->set_video_params(VideoParams(video_params.width(),
+ video_params.height(),
+ video_params.time_base(),
+ output_fmt,
+ video_params.pixel_aspect_ratio(),
+ video_params.interlacing(),
+ video_params.divider()));
+ frame->allocate();
+
+ node->GenerateFrame(frame, job);
+
+ return CachedFrameToTexture(frame);
+}
+
+QVariant RenderProcessor::GetCachedFrame(const Node *node, const rational &time)
+{
+ if (ticket_->property("mode").value() == RenderMode::kOffline
+ && !cache_path_.isEmpty()
+ && node->id() == QStringLiteral("org.olivevideoeditor.Olive.videoinput")) {
+ const VideoParams& video_params = Node::ValueToPtr(ticket_->property("viewer"))->video_params();
+
+ QByteArray hash = RenderManager::Hash(node, video_params, time);
+
+ FramePtr f = FrameHashCache::LoadCacheFrame(cache_path_, hash);
+
+ if (f) {
+ // The cached frame won't load with the correct divider by default, so we enforce it here
+ f->set_video_params(VideoParams(f->width() * video_params.divider(),
+ f->height() * video_params.divider(),
+ f->video_params().time_base(),
+ f->video_params().format(),
+ f->video_params().pixel_aspect_ratio(),
+ f->video_params().interlacing(),
+ video_params.divider()));
+
+ return CachedFrameToTexture(f);
+ }
+ }
+
+ return QVariant();
+}
+
+OLIVE_NAMESPACE_EXIT
diff --git a/app/render/renderprocessor.h b/app/render/renderprocessor.h
new file mode 100644
index 000000000..c7a287b0e
--- /dev/null
+++ b/app/render/renderprocessor.h
@@ -0,0 +1,75 @@
+/***
+
+ 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 RENDERPROCESSOR_H
+#define RENDERPROCESSOR_H
+
+#include "node/traverser.h"
+#include "render/backend/rendercontext.h"
+#include "threading/threadticket.h"
+
+OLIVE_NAMESPACE_ENTER
+
+class RenderProcessor : public NodeTraverser, public QObject
+{
+ Q_OBJECT
+public:
+ static void Process(RenderTicketPtr ticket, RenderContext* render_ctx);
+
+ struct RenderedWaveform {
+ const TrackOutput* track;
+ AudioVisualWaveform waveform;
+ TimeRange range;
+ };
+
+signals:
+ void GeneratedFrame(FramePtr frame);
+
+ void GeneratedAudio(SampleBufferPtr audio);
+
+protected:
+ virtual NodeValueTable GenerateBlockTable(const TrackOutput *track, const TimeRange &range) override;
+
+ virtual QVariant ProcessVideoFootage(StreamPtr stream, const rational &input_time) override;
+
+ virtual QVariant ProcessAudioFootage(StreamPtr stream, const TimeRange &input_time) override;
+
+ virtual QVariant ProcessShader(const Node *node, const TimeRange &range, const ShaderJob& job) override;
+
+ virtual QVariant ProcessSamples(const Node *node, const TimeRange &range, const SampleJob &job) override;
+
+ virtual QVariant ProcessFrameGeneration(const Node *node, const GenerateJob& job) override;
+
+ virtual QVariant GetCachedFrame(const Node *node, const rational &time) override;
+
+private:
+ RenderProcessor(RenderTicketPtr ticket, RenderContext* render_ctx);
+
+ void Run();
+
+ RenderTicketPtr ticket_;
+
+ RenderContext* render_ctx_;
+
+};
+
+OLIVE_NAMESPACE_EXIT
+
+#endif // RENDERPROCESSOR_H
diff --git a/app/threading/threadpool.h b/app/threading/threadpool.h
index b202aeb22..4c9f8d3af 100644
--- a/app/threading/threadpool.h
+++ b/app/threading/threadpool.h
@@ -69,8 +69,6 @@ public:
void RunTicket(RenderTicketPtr ticket);
- void Cancel();
-
protected:
virtual void run() override;