diff --git a/app/core.cpp b/app/core.cpp index 449197683..0a7e9fd9e 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -49,7 +49,6 @@ #include "panel/panelmanager.h" #include "panel/project/project.h" #include "panel/viewer/viewer.h" -#include "render/backend/opengl/opengltexturecache.h" #include "render/colormanager.h" #include "render/diskmanager.h" #include "render/pixelformat.h" @@ -96,8 +95,6 @@ Core *Core::instance() void Core::DeclareTypesForQt() { qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index ae5788929..b6a4753ef 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -102,7 +102,6 @@ void ViewerOutput::ShiftAudioCache(const rational &from, const rational &to) audio_playback_cache_.Shift(from, to); foreach (TrackOutput* track, track_lists_.at(Timeline::kTrackTypeAudio)->GetTracks()) { - QMutexLocker locker(track->waveform_lock()); track->waveform().Shift(from, to); } } diff --git a/app/render/backend/CMakeLists.txt b/app/render/backend/CMakeLists.txt index 65c369e52..de34bd6b3 100644 --- a/app/render/backend/CMakeLists.txt +++ b/app/render/backend/CMakeLists.txt @@ -18,9 +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 + render/backend/renderer.cpp + render/backend/renderer.h + render/backend/rendererthreadwrapper.cpp + render/backend/rendererthreadwrapper.h PARENT_SCOPE ) diff --git a/app/render/backend/opengl/CMakeLists.txt b/app/render/backend/opengl/CMakeLists.txt index 9464d26bd..e2df52f90 100644 --- a/app/render/backend/opengl/CMakeLists.txt +++ b/app/render/backend/opengl/CMakeLists.txt @@ -16,7 +16,7 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} - render/backend/opengl/openglcontext.cpp - render/backend/opengl/openglcontext.h + render/backend/opengl/openglrenderer.cpp + render/backend/opengl/openglrenderer.h PARENT_SCOPE ) diff --git a/app/render/backend/opengl/openglcontext.cpp b/app/render/backend/opengl/openglcontext.cpp deleted file mode 100644 index 5a81f4719..000000000 --- a/app/render/backend/opengl/openglcontext.cpp +++ /dev/null @@ -1,195 +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 "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/openglrenderer.cpp b/app/render/backend/opengl/openglrenderer.cpp new file mode 100644 index 000000000..a37a84055 --- /dev/null +++ b/app/render/backend/opengl/openglrenderer.cpp @@ -0,0 +1,504 @@ +/*** + + 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 "openglrenderer.h" + +#include + +OLIVE_NAMESPACE_ENTER + +OpenGLRenderer::OpenGLRenderer(QObject* parent) : + Renderer(parent), + context_(nullptr) +{ +} + +OpenGLRenderer::~OpenGLRenderer() +{ +} + +void OpenGLRenderer::Init(QOpenGLContext *existing_ctx) +{ + if (context_) { + qCritical() << "Can't initialize already initialized OpenGLRenderer"; + return; + } + + context_ = existing_ctx; +} + +bool OpenGLRenderer::Init() +{ + if (context_) { + qCritical() << "Can't initialize already initialized OpenGLRenderer"; + return false; + } + + surface_.create(); + + context_ = new QOpenGLContext(this); + if (!context_->create()) { + qCritical() << "Failed to create OpenGL context"; + return false; + } + + context_->moveToThread(this->thread()); + + return true; +} + +void OpenGLRenderer::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 OpenGLRenderer::Destroy() +{ + if (context_->parent() == this) { + delete context_; + } + context_ = nullptr; + + qDeleteAll(shader_cache_); + shader_cache_.clear(); + + if (surface_.isValid()) { + surface_.destroy(); + } +} + +QVariant OpenGLRenderer::CreateNativeTexture(const VideoParams &p, void *data, int linesize) +{ + GLuint texture; + functions_->glGenTextures(1, &texture); + + 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 OpenGLRenderer::DestroyNativeTexture(QVariant texture) +{ + GLuint t = texture.value(); + functions_->glDeleteTextures(1, &t); +} + +void OpenGLRenderer::UploadToTexture(Texture *texture, void *data, int linesize) +{ + GLuint t = texture->id().value(); + const VideoParams& p = texture->params(); + + // Store currently bound texture so it can be restored later + GLint current_tex; + functions_->glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_tex); + + functions_->glBindTexture(GL_TEXTURE_2D, 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); + + functions_->glBindTexture(GL_TEXTURE_2D, current_tex); +} + +void OpenGLRenderer::DownloadFromTexture(Texture* texture, void *data, int linesize) +{ + GLuint t = texture->id().value(); + const VideoParams& p = texture->params(); + + GLint current_tex; + functions_->glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_tex); + + functions_->glBindTexture(GL_TEXTURE_2D, 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); + + functions_->glBindTexture(GL_TEXTURE_2D, current_tex); +} + +Renderer::TexturePtr OpenGLRenderer::ProcessShader(const Node *node, const TimeRange &range, const ShaderJob &job, const VideoParams ¶ms) +{ + // 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; + + QString full_shader_id = QStringLiteral("%1:%2").arg(node->id(), job.GetShaderID()); + QOpenGLShaderProgram* shader = shader_cache_.value(full_shader_id); + + if (!shader) { + // Since we have shader code, compile it now + ShaderCode code = node->GetShaderCode(job.GetShaderID()); + 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 = new QOpenGLShaderProgram(this); + 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; + } + + if (!shader) { + // Couldn't find or build the shader required + return nullptr; + } + } + + 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: + { + TexturePtr texture = value.value(); + + if (texture) { + if (PixelFormat::FormatHasAlphaChannel(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->id().value() : 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->width() * texture->divider(); + + // Adjust virtual width by pixel aspect if necessary + if (texture->params().pixel_aspect_ratio() != 1 + || params.pixel_aspect_ratio() != 1) { + double relative_pixel_aspect = 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->height() * 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; + } + + TexturePtr dst_refs[2]; + dst_refs[0] = CreateTexture(output_params); + + // If this node requires multiple iterations, get a texture for it too + if (real_iteration_count > 1) { + dst_refs[1] = CreateTexture(output_params); + } + + // Some nodes use multiple iterations for optimization + TexturePtr 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, 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 output_tex; +} + +/*VideoParams OpenGLRenderer::GetParamsFromTexture(QVariant texture) +{ + GLuint t = texture.value(); + + return texture_params_.value(t); +}*/ + +GLint OpenGLRenderer::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 OpenGLRenderer::GetPixelFormat(PixelFormat::Format format) +{ + if (PixelFormat::FormatHasAlphaChannel(format)) { + return GL_RGBA; + } else { + return GL_RGB; + } +} + +GLenum OpenGLRenderer::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/openglrenderer.h similarity index 56% rename from app/render/backend/opengl/openglcontext.h rename to app/render/backend/opengl/openglrenderer.h index 9c56f428f..5c6bbb1d2 100644 --- a/app/render/backend/opengl/openglcontext.h +++ b/app/render/backend/opengl/openglrenderer.h @@ -23,19 +23,22 @@ #include #include +#include #include -#include "render/backend/rendercontext.h" +#include "render/backend/renderer.h" OLIVE_NAMESPACE_ENTER -class OpenGLContext : public RenderContext +class OpenGLRenderer : public Renderer { Q_OBJECT public: - OpenGLContext(QObject* parent = nullptr); + OpenGLRenderer(QObject* parent = nullptr); - virtual ~OpenGLContext() override; + virtual ~OpenGLRenderer() override; + + void Init(QOpenGLContext* existing_ctx); virtual bool Init() override; @@ -44,15 +47,20 @@ public slots: virtual void Destroy() override; - virtual QVariant CreateTexture(const VideoParams& param, void* data, int linesize) override; + virtual QVariant CreateNativeTexture(const VideoParams& p, void* data = nullptr, int linesize = 0) override; - virtual void DestroyTexture(QVariant texture) override; + virtual void DestroyNativeTexture(QVariant texture) override; - virtual void UploadToTexture(QVariant texture, void* data, int linesize) override; + virtual void UploadToTexture(Texture* texture, void* data, int linesize) override; - virtual void DownloadFromTexture(QVariant texture, void* data, int linesize) override; + virtual void DownloadFromTexture(Texture* texture, void* data, int linesize) override; - virtual VideoParams GetParamsFromTexture(QVariant texture) override; + virtual TexturePtr ProcessShader(const OLIVE_NAMESPACE::Node* node, + const OLIVE_NAMESPACE::TimeRange &range, + const OLIVE_NAMESPACE::ShaderJob &job, + const OLIVE_NAMESPACE::VideoParams ¶ms) override; + + virtual QVariant TransformColor(QVariant texture, ColorProcessorPtr processor) override; private: static GLint GetInternalFormat(PixelFormat::Format format); @@ -67,10 +75,12 @@ private: QOffscreenSurface surface_; - QMap texture_params_; + QHash shader_cache_; }; OLIVE_NAMESPACE_EXIT +Q_DECLARE_METATYPE(OLIVE_NAMESPACE::OpenGLRenderer::TexturePtr); + #endif // OPENGLCONTEXT_H diff --git a/app/render/backend/opengl/openglshader.cpp b/app/render/backend/opengl/openglshader.cpp new file mode 100644 index 000000000..7e76f619a --- /dev/null +++ b/app/render/backend/opengl/openglshader.cpp @@ -0,0 +1,254 @@ +/*** + + 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 new file mode 100644 index 000000000..452dc3c3d --- /dev/null +++ b/app/render/backend/opengl/openglshader.h @@ -0,0 +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 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/rendercontext.h b/app/render/backend/rendercontext.h deleted file mode 100644 index dba4a9e51..000000000 --- a/app/render/backend/rendercontext.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 RENDERCONTEXT_H -#define RENDERCONTEXT_H - -#include -#include - -#include "common/define.h" -#include "render/videoparams.h" - -OLIVE_NAMESPACE_ENTER - -class RenderContext : public QObject -{ - Q_OBJECT -public: - 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/rendercontext.cpp b/app/render/backend/renderer.cpp similarity index 71% rename from app/render/backend/rendercontext.cpp rename to app/render/backend/renderer.cpp index 2f07702a9..a1ddaa924 100644 --- a/app/render/backend/rendercontext.cpp +++ b/app/render/backend/renderer.cpp @@ -18,19 +18,25 @@ ***/ -#include "rendercontext.h" +#include "renderer.h" OLIVE_NAMESPACE_ENTER -RenderContext::RenderContext(QObject *parent) : +Renderer::Renderer(QObject *parent) : QObject(parent) { } -QVariant RenderContext::CreateTexture(const VideoParams ¶m) +Renderer::TexturePtr Renderer::CreateTexture(const VideoParams ¶m, void *data, int linesize) { - return CreateTexture(param, nullptr, 0); + QVariant v = CreateNativeTexture(param, data, linesize); + + if (v.isNull()) { + return nullptr; + } + + return std::make_shared(this, v, param); } OLIVE_NAMESPACE_EXIT diff --git a/app/render/backend/renderer.h b/app/render/backend/renderer.h new file mode 100644 index 000000000..7f3e3d334 --- /dev/null +++ b/app/render/backend/renderer.h @@ -0,0 +1,148 @@ +/*** + + 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 + +#include "common/define.h" +#include "common/timerange.h" +#include "node/node.h" +#include "render/colorprocessor.h" +#include "render/videoparams.h" + +OLIVE_NAMESPACE_ENTER + +class Renderer : public QObject +{ + Q_OBJECT +public: + Renderer(QObject* parent = nullptr); + + virtual ~Renderer() override; + + virtual bool Init() = 0; + + class Texture + { + public: + Texture(Renderer* renderer, const QVariant& native, const VideoParams& param) : + renderer_(renderer), + params_(param), + id_(native) + { + } + + ~Texture() + { + renderer_->DestroyNativeTexture(id_); + } + + QVariant id() const + { + return id_; + } + + const VideoParams& params() const + { + return params_; + } + + void Upload(void* data, int linesize = 0) + { + renderer_->UploadToTexture(this, data, linesize); + } + + int width() const + { + return params_.width(); + } + + int height() const + { + return params_.height(); + } + + PixelFormat::Format format() const + { + return params_.format(); + } + + int divider() const + { + return params_.divider(); + } + + const rational& pixel_aspect_ratio() const + { + return params_.pixel_aspect_ratio(); + } + + private: + Renderer* renderer_; + + VideoParams params_; + + QVariant id_; + + }; + + using TexturePtr = std::shared_ptr; + + TexturePtr CreateTexture(const VideoParams& param, void* data = nullptr, int linesize = 0); + +public slots: + virtual void PostInit() = 0; + + virtual void Destroy() = 0; + + virtual QVariant CreateNativeTexture(const VideoParams& param, void* data = nullptr, int linesize = 0) = 0; + + virtual void DestroyNativeTexture(QVariant texture) = 0; + + virtual QVariant CreateNativeShader(const ShaderCode& code) = 0; + + virtual void DestroyNativeShader(QVariant shader) = 0; + + virtual void UploadToTexture(Texture* texture, void* data, int linesize) = 0; + + virtual void DownloadFromTexture(Texture* texture, void* data, int linesize) = 0; + + virtual TexturePtr ProcessShader(const OLIVE_NAMESPACE::Node* node, + const OLIVE_NAMESPACE::TimeRange &range, + const OLIVE_NAMESPACE::ShaderJob &job, + const OLIVE_NAMESPACE::VideoParams ¶ms) = 0; + + virtual QVariant TransformColor(QVariant texture, OLIVE_NAMESPACE::ColorProcessorPtr processor) = 0; + + virtual void Render() = 0; + + virtual void RenderToTexture(Texture* destination) = 0; + +private: + + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // RENDERCONTEXT_H diff --git a/app/render/backend/rendercontextthreadwrapper.cpp b/app/render/backend/rendererthreadwrapper.cpp similarity index 50% rename from app/render/backend/rendercontextthreadwrapper.cpp rename to app/render/backend/rendererthreadwrapper.cpp index 397189809..d344b16c5 100644 --- a/app/render/backend/rendercontextthreadwrapper.cpp +++ b/app/render/backend/rendererthreadwrapper.cpp @@ -18,20 +18,25 @@ ***/ -#include "rendercontextthreadwrapper.h" +#include "rendererthreadwrapper.h" OLIVE_NAMESPACE_ENTER -RenderContextThreadWrapper::RenderContextThreadWrapper(RenderContext *inner, QObject *parent) : - RenderContext(parent), +/*RendererThreadWrapper::RendererThreadWrapper(Renderer *inner, QObject *parent) : + Renderer(parent), inner_(inner), thread_(nullptr) { inner_->setParent(this); } -bool RenderContextThreadWrapper::Init() +bool RendererThreadWrapper::Init() { + // Init context in main thread + if (!inner_->Init()) { + return false; + } + // Create thread QThread* thread = new QThread(this); thread->start(QThread::IdlePriority); @@ -39,17 +44,16 @@ bool RenderContextThreadWrapper::Init() // 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); + QMetaObject::invokeMethod(inner_, "PostInit", Qt::BlockingQueuedConnection); + + return true; } -void RenderContextThreadWrapper::Destroy() +void RendererThreadWrapper::Destroy() { if (thread_) { - QMetaObject::invokeMethod(inner_, "Destroy", Qt::QueuedConnection); + QMetaObject::invokeMethod(inner_, "Destroy", Qt::BlockingQueuedConnection); thread_->quit(); thread_->wait(); @@ -58,7 +62,7 @@ void RenderContextThreadWrapper::Destroy() } } -QVariant RenderContextThreadWrapper::CreateTexture(const VideoParams ¶m, void *data, int linesize) +QVariant RendererThreadWrapper::CreateTexture(const VideoParams ¶m, void *data, int linesize) { QVariant v; @@ -71,29 +75,55 @@ QVariant RenderContextThreadWrapper::CreateTexture(const VideoParams ¶m, voi return v; } -void RenderContextThreadWrapper::DestroyTexture(QVariant texture) +void RendererThreadWrapper::DestroyTexture(QVariant texture) { - QMetaObject::invokeMethod(inner_, "DestroyTexture", Qt::QueuedConnection, + QMetaObject::invokeMethod(inner_, "DestroyTexture", Qt::BlockingQueuedConnection, Q_ARG(QVariant, texture)); } -void RenderContextThreadWrapper::UploadToTexture(QVariant texture, void *data, int linesize) +void RendererThreadWrapper::UploadToTexture(QVariant texture, void *data, int linesize) { - QMetaObject::invokeMethod(inner_, "UploadToTexture", Qt::QueuedConnection, + QMetaObject::invokeMethod(inner_, "UploadToTexture", Qt::BlockingQueuedConnection, Q_ARG(QVariant, texture), Q_ARG(void*, data), Q_ARG(int, linesize)); } -void RenderContextThreadWrapper::DownloadFromTexture(QVariant texture, void *data, int linesize) +void RendererThreadWrapper::DownloadFromTexture(QVariant texture, void *data, int linesize) { - QMetaObject::invokeMethod(inner_, "DownloadFromTexture", Qt::QueuedConnection, + QMetaObject::invokeMethod(inner_, "DownloadFromTexture", Qt::BlockingQueuedConnection, Q_ARG(QVariant, texture), Q_ARG(void*, data), Q_ARG(int, linesize)); } -VideoParams RenderContextThreadWrapper::GetParamsFromTexture(QVariant texture) +QVariant RendererThreadWrapper::ProcessShader(const Node *node, const TimeRange &range, const ShaderJob &job, const VideoParams ¶ms) +{ + QVariant v; + + QMetaObject::invokeMethod(inner_, "ProcessShader", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QVariant, v), + OLIVE_NS_CONST_ARG(Node*, node), + OLIVE_NS_CONST_ARG(TimeRange&, range), + OLIVE_NS_CONST_ARG(ShaderJob&, job), + OLIVE_NS_CONST_ARG(VideoParams&, params)); + + return v; +} + +QVariant RendererThreadWrapper::TransformColor(QVariant texture, ColorProcessorPtr processor) +{ + QVariant v; + + QMetaObject::invokeMethod(inner_, "ProcessShader", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QVariant, v), + Q_ARG(QVariant, texture), + OLIVE_NS_ARG(ColorProcessorPtr, processor)); + + return v; +} + +VideoParams RendererThreadWrapper::GetParamsFromTexture(QVariant texture) { VideoParams p; @@ -102,6 +132,6 @@ VideoParams RenderContextThreadWrapper::GetParamsFromTexture(QVariant texture) Q_ARG(QVariant, texture)); return p; -} +}*/ OLIVE_NAMESPACE_EXIT diff --git a/app/render/backend/rendercontextthreadwrapper.h b/app/render/backend/rendererthreadwrapper.h similarity index 66% rename from app/render/backend/rendercontextthreadwrapper.h rename to app/render/backend/rendererthreadwrapper.h index 1a8a7f6c1..5a0e8d5cb 100644 --- a/app/render/backend/rendercontextthreadwrapper.h +++ b/app/render/backend/rendererthreadwrapper.h @@ -23,16 +23,16 @@ #include -#include "rendercontext.h" +#include "renderer.h" OLIVE_NAMESPACE_ENTER -class RenderContextThreadWrapper : public RenderContext +/*class RendererThreadWrapper : public Renderer { public: - RenderContextThreadWrapper(RenderContext* inner, QObject* parent = nullptr); + RendererThreadWrapper(Renderer* inner, QObject* parent = nullptr); - virtual ~RenderContextThreadWrapper() override + virtual ~RendererThreadWrapper() override { Destroy(); } @@ -52,14 +52,22 @@ public slots: virtual void DownloadFromTexture(QVariant texture, void* data, int linesize) override; - virtual VideoParams GetParamsFromTexture(QVariant texture) override; + virtual QVariant ProcessShader(const OLIVE_NAMESPACE::Node* node, + const OLIVE_NAMESPACE::TimeRange &range, + const OLIVE_NAMESPACE::ShaderJob &job, + const OLIVE_NAMESPACE::VideoParams ¶ms) override; + + virtual QVariant TransformColor(QVariant texture, + OLIVE_NAMESPACE::ColorProcessorPtr processor) override; + + //virtual VideoParams GetParamsFromTexture(QVariant texture) override; private: - RenderContext* inner_; + Renderer* inner_; QThread* thread_; -}; +};*/ OLIVE_NAMESPACE_EXIT diff --git a/app/render/rendermanager.cpp b/app/render/rendermanager.cpp index 546b90784..9979b2b29 100644 --- a/app/render/rendermanager.cpp +++ b/app/render/rendermanager.cpp @@ -27,8 +27,8 @@ #include "config/config.h" #include "core.h" -#include "render/backend/opengl/openglcontext.h" -#include "render/backend/rendercontextthreadwrapper.h" +#include "render/backend/opengl/openglrenderer.h" +#include "render/backend/rendererthreadwrapper.h" #include "renderprocessor.h" #include "task/conform/conform.h" #include "task/taskmanager.h" @@ -41,7 +41,7 @@ RenderManager* RenderManager::instance_ = nullptr; RenderManager::RenderManager(QObject *parent) : ThreadPool(QThread::IdlePriority, 0, parent) { - context_ = new RenderContextThreadWrapper(new OpenGLContext(), this); + context_ = new RendererThreadWrapper(new OpenGLRenderer(), this); context_->Init(); } diff --git a/app/render/rendermanager.h b/app/render/rendermanager.h index 68e26905b..f00b6d2b0 100644 --- a/app/render/rendermanager.h +++ b/app/render/rendermanager.h @@ -30,7 +30,7 @@ #include "node/graph.h" #include "node/output/viewer/viewer.h" #include "node/traverser.h" -#include "render/backend/rendercontext.h" +#include "render/backend/renderer.h" #include "threading/threadpool.h" OLIVE_NAMESPACE_ENTER @@ -111,12 +111,12 @@ private: static RenderManager* instance_; - RenderContext* context_; + Renderer* context_; }; -Q_DECLARE_METATYPE(RenderManager::TicketType); - OLIVE_NAMESPACE_EXIT +Q_DECLARE_METATYPE(OLIVE_NAMESPACE::RenderManager::TicketType); + #endif // RENDERBACKEND_H diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index b3c354c05..dc6d898d4 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -20,11 +20,15 @@ #include "renderprocessor.h" +#include +#include +#include + #include "rendermanager.h" OLIVE_NAMESPACE_ENTER -RenderProcessor::RenderProcessor(RenderTicketPtr ticket, RenderContext *render_ctx) : +RenderProcessor::RenderProcessor(RenderTicketPtr ticket, Renderer *render_ctx) : ticket_(ticket), render_ctx_(render_ctx) { @@ -46,7 +50,7 @@ void RenderProcessor::Run() NodeValueTable table = ProcessInput(viewer->texture_input(), TimeRange(time, time + viewer->video_params().time_base())); - QVariant texture = table.Get(NodeParam::kTexture); + Renderer::TexturePtr texture = table.Get(NodeParam::kTexture).value(); QSize frame_size = ticket_->property("size").value(); if (frame_size.isNull()) { @@ -65,18 +69,18 @@ void RenderProcessor::Run() viewer->video_params().divider())); frame->allocate(); - if (texture.isNull()) { + if (!texture) { // Blank frame out memset(frame->data(), 0, frame->allocated_size()); } else { // Dump texture contents to frame - VideoParams tex_params = render_ctx_->GetParamsFromTexture(texture); + const VideoParams& tex_params = texture->params(); if (tex_params.width() != frame->width() || tex_params.height() != frame->height()) { // FIXME: Blit this shit } - render_ctx_->DownloadFromTexture(texture, frame->data(), frame->linesize_pixels()); + render_ctx_->DownloadFromTexture(texture.get(), frame->data(), frame->linesize_pixels()); } ticket_->Finish(QVariant::fromValue(frame), IsCancelled()); @@ -109,7 +113,7 @@ void RenderProcessor::Run() this->deleteLater(); } -void RenderProcessor::Process(RenderTicketPtr ticket, RenderContext *render_ctx) +void RenderProcessor::Process(RenderTicketPtr ticket, Renderer *render_ctx) { RenderProcessor p(ticket, render_ctx); p.Run(); @@ -293,240 +297,9 @@ QVariant RenderProcessor::ProcessAudioFootage(StreamPtr stream, const TimeRange 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; + const VideoParams& video_params = Node::ValueToPtr(ticket_->property("viewer"))->video_params(); - 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); + render_ctx_->ProcessShader(node, range, job, video_params); } QVariant RenderProcessor::ProcessSamples(const Node *node, const TimeRange &range, const SampleJob &job) diff --git a/app/render/renderprocessor.h b/app/render/renderprocessor.h index c7a287b0e..f41d4961d 100644 --- a/app/render/renderprocessor.h +++ b/app/render/renderprocessor.h @@ -22,16 +22,16 @@ #define RENDERPROCESSOR_H #include "node/traverser.h" -#include "render/backend/rendercontext.h" +#include "render/backend/renderer.h" #include "threading/threadticket.h" OLIVE_NAMESPACE_ENTER -class RenderProcessor : public NodeTraverser, public QObject +class RenderProcessor : public QObject, public NodeTraverser { Q_OBJECT public: - static void Process(RenderTicketPtr ticket, RenderContext* render_ctx); + static void Process(RenderTicketPtr ticket, Renderer* render_ctx); struct RenderedWaveform { const TrackOutput* track; @@ -60,16 +60,18 @@ protected: virtual QVariant GetCachedFrame(const Node *node, const rational &time) override; private: - RenderProcessor(RenderTicketPtr ticket, RenderContext* render_ctx); + RenderProcessor(RenderTicketPtr ticket, Renderer* render_ctx); void Run(); RenderTicketPtr ticket_; - RenderContext* render_ctx_; + Renderer* render_ctx_; }; OLIVE_NAMESPACE_EXIT +Q_DECLARE_METATYPE(OLIVE_NAMESPACE::RenderProcessor::RenderedWaveform); + #endif // RENDERPROCESSOR_H diff --git a/app/widget/manageddisplay/manageddisplay.cpp b/app/widget/manageddisplay/manageddisplay.cpp index 30574363a..52ba3f356 100644 --- a/app/widget/manageddisplay/manageddisplay.cpp +++ b/app/widget/manageddisplay/manageddisplay.cpp @@ -22,6 +22,8 @@ #include +#include "render/backend/opengl/openglrenderer.h" + OLIVE_NAMESPACE_ENTER ManagedDisplayWidget::ManagedDisplayWidget(QWidget *parent) : @@ -30,6 +32,8 @@ ManagedDisplayWidget::ManagedDisplayWidget(QWidget *parent) : color_service_(nullptr) { setContextMenuPolicy(Qt::CustomContextMenu); + + attached_renderer_ = new OpenGLRenderer(); } ManagedDisplayWidget::~ManagedDisplayWidget() @@ -102,7 +106,7 @@ void ManagedDisplayWidget::ColorConfigChanged() SetColorTransform(color_manager_->GetCompliantColorSpace(color_transform_, true)); } -OpenGLColorProcessorPtr ManagedDisplayWidget::color_service() +ColorProcessorPtr ManagedDisplayWidget::color_service() { return color_service_; } @@ -113,6 +117,8 @@ void ManagedDisplayWidget::ContextCleanup() color_service_ = nullptr; + attached_renderer_->Destroy(); + doneCurrent(); } @@ -188,6 +194,8 @@ void ManagedDisplayWidget::initializeGL() SetupColorProcessor(); connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ManagedDisplayWidget::ContextCleanup, Qt::DirectConnection); + + static_cast(attached_renderer_)->Init(context()); } void ManagedDisplayWidget::EnableDefaultContextMenu() @@ -280,11 +288,9 @@ void ManagedDisplayWidget::SetupColorProcessor() try { - color_service_ = OpenGLColorProcessor::Create(color_manager_, - color_manager_->GetReferenceColorSpace(), - color_transform_); - - color_service_->Enable(context(), true); + color_service_ = ColorProcessor::Create(color_manager_, + color_manager_->GetReferenceColorSpace(), + color_transform_); } catch (OCIO::Exception& e) { diff --git a/app/widget/manageddisplay/manageddisplay.h b/app/widget/manageddisplay/manageddisplay.h index c03d5ea71..ec524faeb 100644 --- a/app/widget/manageddisplay/manageddisplay.h +++ b/app/widget/manageddisplay/manageddisplay.h @@ -23,7 +23,7 @@ #include -#include "render/backend/opengl/openglcolorprocessor.h" +#include "render/backend/renderer.h" #include "render/colormanager.h" #include "widget/menu/menu.h" @@ -98,7 +98,7 @@ protected: /** * @brief Provides access to the color processor (nullptr if none is set) */ - OpenGLColorProcessorPtr color_service(); + ColorProcessorPtr color_service(); /** * @brief Override when setting up OpenGL context @@ -128,6 +128,11 @@ private: */ void ClearOCIOLutTexture(); + /** + * @brief Renderer abstraction + */ + Renderer* attached_renderer_; + /** * @brief Connected color manager */ @@ -136,7 +141,7 @@ private: /** * @brief Color management service */ - OpenGLColorProcessorPtr color_service_; + ColorProcessorPtr color_service_; /** * @brief Internal color transform storage diff --git a/app/widget/scope/histogram/histogram.cpp b/app/widget/scope/histogram/histogram.cpp index e89918c06..b88c3996d 100644 --- a/app/widget/scope/histogram/histogram.cpp +++ b/app/widget/scope/histogram/histogram.cpp @@ -25,7 +25,6 @@ #include "common/qtutils.h" #include "node/node.h" -#include "render/backend/opengl/openglrenderfunctions.h" OLIVE_NAMESPACE_ENTER @@ -75,7 +74,7 @@ void HistogramScope::CleanUp() doneCurrent(); } -OpenGLShaderPtr HistogramScope::CreateShader() +QVariant HistogramScope::CreateShader() { OpenGLShaderPtr pipeline = OpenGLShader::Create(); diff --git a/app/widget/scope/histogram/histogram.h b/app/widget/scope/histogram/histogram.h index 70751355f..8de6e4da4 100644 --- a/app/widget/scope/histogram/histogram.h +++ b/app/widget/scope/histogram/histogram.h @@ -36,16 +36,16 @@ public: protected: virtual void initializeGL() override; - virtual OpenGLShaderPtr CreateShader() override; - OpenGLShaderPtr CreateSecondaryShader(); + virtual QVariant CreateShader() override; + QVariant CreateSecondaryShader(); void AssertAdditionalTextures(); virtual void DrawScope() override; private: - OpenGLShaderPtr pipeline_secondary_; - OpenGLTexture texture_row_sums_; + QVariant pipeline_secondary_; + QVariant texture_row_sums_; private slots: void CleanUp(); diff --git a/app/widget/scope/scopebase/scopebase.cpp b/app/widget/scope/scopebase/scopebase.cpp index e3b65d48c..38aa84e13 100644 --- a/app/widget/scope/scopebase/scopebase.cpp +++ b/app/widget/scope/scopebase/scopebase.cpp @@ -54,7 +54,7 @@ void ScopeBase::showEvent(QShowEvent* e) UploadTextureFromBuffer(); } -OpenGLShaderPtr ScopeBase::CreateShader() +QVariant ScopeBase::CreateShader() { return OpenGLShader::CreateDefault(); } diff --git a/app/widget/scope/scopebase/scopebase.h b/app/widget/scope/scopebase/scopebase.h index 41e2edefc..dc48c20f3 100644 --- a/app/widget/scope/scopebase/scopebase.h +++ b/app/widget/scope/scopebase/scopebase.h @@ -22,10 +22,7 @@ #define SCOPEBASE_H #include "codec/frame.h" -#include "render/backend/opengl/openglcolorprocessor.h" -#include "render/backend/opengl/openglframebuffer.h" -#include "render/backend/opengl/openglshader.h" -#include "render/backend/opengl/opengltexture.h" +#include "render/colorprocessor.h" #include "widget/manageddisplay/manageddisplay.h" OLIVE_NAMESPACE_ENTER @@ -47,35 +44,28 @@ protected: virtual void showEvent(QShowEvent* e) override; - virtual OpenGLShaderPtr CreateShader(); + virtual QVariant CreateShader(); virtual void DrawScope(); - OpenGLShaderPtr pipeline() + QVariant pipeline() { return pipeline_; } - OpenGLTexture& managed_tex() + QVariant managed_tex() { return managed_tex_; } - OpenGLFramebuffer& framebuffer() - { - return framebuffer_; - } - private: void UploadTextureFromBuffer(); - OpenGLShaderPtr pipeline_; + QVariant pipeline_; - OpenGLTexture texture_; + QVariant texture_; - OpenGLTexture managed_tex_; - - OpenGLFramebuffer framebuffer_; + QVariant managed_tex_; Frame* buffer_; diff --git a/app/widget/viewer/viewerdisplay.h b/app/widget/viewer/viewerdisplay.h index 0f8f6866f..430d8cbf7 100644 --- a/app/widget/viewer/viewerdisplay.h +++ b/app/widget/viewer/viewerdisplay.h @@ -25,9 +25,7 @@ #include "node/node.h" #include "render/backend/opengl/openglcolorprocessor.h" -#include "render/backend/opengl/openglframebuffer.h" #include "render/backend/opengl/openglshader.h" -#include "render/backend/opengl/opengltexture.h" #include "render/color.h" #include "render/colormanager.h" #include "tool/tool.h" @@ -211,7 +209,7 @@ private: /** * @brief Internal reference to the OpenGL texture to draw. Set in SetTexture() and used in paintGL(). */ - OpenGLTexture texture_; + QVariant texture_; /** * @brief Translation only matrix (defaults to identity).