restored previous renderer iteration for porting

This commit is contained in:
itsmattkc
2019-10-31 20:06:11 +11:00
parent 77bcb70dac
commit 55f7d6aa8a
16 changed files with 502 additions and 118 deletions
+1 -12
View File
@@ -8,10 +8,7 @@
#include "project/item/footage/footage.h"
#include "render/pixelservice.h"
VideoInput::VideoInput() :
color_processor_(nullptr),
pipeline_(nullptr),
ocio_texture_(0)
VideoInput::VideoInput()
{
matrix_input_ = new NodeInput("matrix_in");
matrix_input_->set_data_type(NodeInput::kMatrix);
@@ -45,14 +42,6 @@ QString VideoInput::Description()
void VideoInput::Release()
{
MediaInput::Release();
internal_tex_.Destroy();
color_processor_ = nullptr;
pipeline_ = nullptr;
if (ocio_texture_ != 0) {
ocio_ctx_->functions()->glDeleteTextures(1, &ocio_texture_);
}
}
NodeInput *VideoInput::matrix_input()
+4
View File
@@ -31,5 +31,9 @@ set(OLIVE_SOURCES
render/backend/videorendererdownloadthread.cpp
render/backend/videorendererprocessthread.h
render/backend/videorendererprocessthread.cpp
render/backend/videorendererthreadbase.h
render/backend/videorendererthreadbase.cpp
render/backend/renderinstance.h
render/backend/renderinstance.cpp
PARENT_SCOPE
)
@@ -4,3 +4,9 @@ AudioRenderBackend::AudioRenderBackend()
{
}
void AudioRenderBackend::InvalidateCache(const rational &start_range, const rational &end_range)
{
Q_UNUSED(start_range)
Q_UNUSED(end_range)
}
+113
View File
@@ -1,5 +1,7 @@
#include "openglshader.h"
#include <QOpenGLExtraFunctions>
OpenGLShader::OpenGLShader()
{
@@ -17,6 +19,117 @@ OpenGLShaderPtr OpenGLShader::CreateDefault(const QString &function_name, const
return program;
}
// copied from source code to OCIODisplay
const int OCIO_LUT3D_EDGE_SIZE = 32;
// copied from source code to OCIODisplay, expanded from 3*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE
const int OCIO_NUM_3D_ENTRIES = 98304;
OpenGLShaderPtr OpenGLShader::CreateOCIO(QOpenGLContext* ctx,
GLuint& lut_texture,
OCIO::ConstProcessorRcPtr processor,
bool alpha_is_associated)
{
QOpenGLExtraFunctions* xf = ctx->extraFunctions();
// Create LUT texture
xf->glGenTextures(1, &lut_texture);
// Bind LUT
xf->glBindTexture(GL_TEXTURE_3D, lut_texture);
// Set texture parameters
xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
// Allocate storage for texture
xf->glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F_ARB,
OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE,
0, GL_RGB,GL_FLOAT, nullptr);
//
// SET UP GLSL SHADER
//
OCIO::GpuShaderDesc shaderDesc;
const char* ocio_func_name = "OCIODisplay";
shaderDesc.setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_0);
shaderDesc.setFunctionName(ocio_func_name);
shaderDesc.setLut3DEdgeLen(OCIO_LUT3D_EDGE_SIZE);
//
// COMPUTE 3D LUT
//
GLfloat* ocio_lut_data = new GLfloat[OCIO_NUM_3D_ENTRIES];
processor->getGpuLut3D(ocio_lut_data, shaderDesc);
// Upload LUT data to texture
xf->glTexSubImage3D(GL_TEXTURE_3D, 0,
0, 0, 0,
OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE,
GL_RGB, GL_FLOAT, ocio_lut_data);
delete [] ocio_lut_data;
// Create OCIO shader code
QString shader_text(processor->getGpuShaderText(shaderDesc));
QString shader_call;
// Enforce alpha association
if (alpha_is_associated) {
// If alpha is already associated, we'll need to disassociate and reassociate
shader_text.append("\n");
QString disassociate_func_name = "disassoc";
shader_text.append(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 = QString("%3(%1(%2(col), tex2));").arg(ocio_func_name,
disassociate_func_name,
reassociate_func_name);
} else {
// If alpha is not already associated, we can just associate after OCIO
// Add associate function
QString associate_func_name = "assoc";
shader_text.append(CodeAlphaAssociate(associate_func_name));
// Make OCIO call pass through associate function
shader_call = QString("%2(%1(col, tex2));").arg(ocio_func_name, associate_func_name);
}
// Add process() function, which GetPipeline() will call if specified
QString process_function_name = "process";
shader_text.append(QString("\n"
"uniform sampler3D tex2;\n"
"\n"
"vec4 %2(vec4 col) {\n"
" return %1\n"
"}\n").arg(shader_call, process_function_name));
// Get pipeline-based shader to inject OCIO shader into
OpenGLShaderPtr shader = OpenGLShader::CreateDefault(process_function_name, shader_text);
// Release LUT
xf->glBindTexture(GL_TEXTURE_3D, 0);
return shader;
}
QString OpenGLShader::CodeDefaultFragment(const QString &function_name, const QString &shader_code)
{
QString frag_code = QStringLiteral("#version 110\n"
+8
View File
@@ -4,6 +4,9 @@
#include <memory>
#include <QOpenGLShaderProgram>
#include <OpenColorIO/OpenColorIO.h>
namespace OCIO = OCIO_NAMESPACE::v1;
class OpenGLShader;
using OpenGLShaderPtr = std::shared_ptr<OpenGLShader>;
@@ -14,6 +17,11 @@ public:
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(const QString &function_name = QString(),
const QString &shader_code = QString());
static QString CodeDefaultVertex();
-68
View File
@@ -30,74 +30,6 @@ void RenderBackend::SetViewerNode(ViewerOutput *viewer_node)
Decompile();
}
void RenderBackend::InvalidateCache(const rational &start_range, const rational &end_range)
{
if (!params_.is_valid()) {
return;
}
// Adjust range to min/max values
rational start_range_adj = qMax(rational(0), start_range);
rational end_range_adj = qMin(viewer_node_->Length(), end_range);
qDebug() << "Cache invalidated between"
<< start_range_adj.toDouble()
<< "and"
<< end_range_adj.toDouble();
// Snap start_range to timebase
double start_range_dbl = start_range_adj.toDouble();
double start_range_numf = start_range_dbl * static_cast<double>(params_.time_base().denominator());
int64_t start_range_numround = qFloor(start_range_numf/static_cast<double>(params_.time_base().numerator())) * params_.time_base().numerator();
rational true_start_range(start_range_numround, params_.time_base().denominator());
for (rational r=true_start_range;r<=end_range_adj;r+=params_.time_base()) {
// Try to order the queue from closest to the playhead to furthest
rational last_time = last_time_requested_;
rational diff = r - last_time;
if (diff < 0) {
// FIXME: Hardcoded number
// If the number is before the playhead, we still prioritize its closeness but not nearly as much (5:1 in this
// example)
diff = qAbs(diff) * 5;
}
bool contains = false;
bool added = false;
QLinkedList<rational>::iterator insert_iterator;
for (QLinkedList<rational>::iterator i = cache_queue_.begin();i != cache_queue_.end();i++) {
rational compare = *i;
if (!added) {
rational compare_diff = compare - last_time;
if (compare_diff > diff) {
insert_iterator = i;
added = true;
}
}
if (compare == r) {
contains = true;
break;
}
}
if (!contains) {
if (added) {
cache_queue_.insert(insert_iterator, r);
} else {
cache_queue_.append(r);
}
}
}
CacheNext();
}
void RenderBackend::SetError(const QString &error)
{
error_ = error;
+125
View File
@@ -0,0 +1,125 @@
/***
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 <http://www.gnu.org/licenses/>.
***/
#include "renderinstance.h"
#include <QDebug>
#include "opengl/openglshader.h"
RenderInstance::RenderInstance(const VideoRenderingParams& params) :
share_ctx_(nullptr),
params_(params)
{
// Create offscreen surface
surface_.create();
}
RenderInstance::~RenderInstance()
{
// Destroy offscreen surface
surface_.destroy();
}
void RenderInstance::SetShareContext(QOpenGLContext *share)
{
Q_ASSERT(!IsStarted());
share_ctx_ = share;
}
bool RenderInstance::Start()
{
if (IsStarted()) {
return true;
}
// Create context object
ctx_ = new QOpenGLContext();
// If we're sharing resources, set this up now
if (share_ctx_ != nullptr) {
ctx_->setShareContext(share_ctx_);
}
// Create OpenGL context (automatically destroys any existing if there is one)
if (!ctx_->create()) {
qWarning() << tr("Failed to create OpenGL context in thread %1").arg(reinterpret_cast<quintptr>(this));
return false;
}
// Make context current on that surface
if (!ctx_->makeCurrent(&surface_)) {
qWarning() << tr("Failed to makeCurrent() on offscreen surface in thread %1").arg(reinterpret_cast<quintptr>(this));
return false;
}
buffer_.Create(ctx_);
// Set viewport to the compositing dimensions
ctx_->functions()->glViewport(0, 0, params_.effective_width(), params_.effective_height());
ctx_->functions()->glEnable(GL_BLEND);
// Set up default pipeline
default_pipeline_ = OpenGLShader::CreateDefault();
return true;
}
void RenderInstance::Stop()
{
if (IsStarted()) {
return;
}
// Destroy pipeline
default_pipeline_ = nullptr;
// Destroy buffer
buffer_.Destroy();
// Destroy context
delete ctx_;
}
bool RenderInstance::IsStarted()
{
return buffer_.IsCreated();
}
OpenGLFramebuffer *RenderInstance::buffer()
{
return &buffer_;
}
QOpenGLContext *RenderInstance::context()
{
return ctx_;
}
const VideoRenderingParams &RenderInstance::params() const
{
return params_;
}
OpenGLShaderPtr RenderInstance::default_pipeline() const
{
return default_pipeline_;
}
+80
View File
@@ -0,0 +1,80 @@
/***
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 <http://www.gnu.org/licenses/>.
***/
#ifndef GLINSTANCE_H
#define GLINSTANCE_H
#include <QMatrix4x4>
#include <QOffscreenSurface>
#include <QOpenGLContext>
#include "opengl/openglshader.h"
#include "opengl/openglframebuffer.h"
#include "render/rendermodes.h"
#include "render/videoparams.h"
/**
* @brief An object containing all resources necessary for each thread to support hardware accelerated rendering
*
* RenderInstance contains everything that Nodes will need to draw with on a per-thread basis.
*
* Due to its usage of QOffscreenSurface, a RenderInstance instance must be constructed in the main (GUI) thread. From
* there it is safe to call Start() on in another thread.
*/
class RenderInstance : public QObject
{
public:
RenderInstance(const VideoRenderingParams &params);
virtual ~RenderInstance() override;
Q_DISABLE_COPY_MOVE(RenderInstance)
void SetShareContext(QOpenGLContext* share);
bool Start();
void Stop();
bool IsStarted();
OpenGLFramebuffer* buffer();
QOpenGLContext* context();
const VideoRenderingParams& params() const;
OpenGLShaderPtr default_pipeline() const;
private:
QOpenGLContext* ctx_;
QOpenGLContext* share_ctx_;
QOffscreenSurface surface_;
OpenGLFramebuffer buffer_;
VideoRenderingParams params_;
OpenGLShaderPtr default_pipeline_;
};
#endif // GLINSTANCE_H
+4 -21
View File
@@ -18,7 +18,7 @@
***/
#include "videorenderer.h"
#include "videorenderbackend.h"
#include <OpenImageIO/imageio.h>
#include <QApplication>
@@ -29,8 +29,7 @@
#include <QtMath>
#include "common/filefunctions.h"
#include "render/gl/functions.h"
#include "render/gl/shadergenerators.h"
#include "opengl/functions.h"
#include "render/pixelservice.h"
VideoRendererProcessor::VideoRendererProcessor(QObject *parent) :
@@ -200,13 +199,13 @@ void VideoRendererProcessor::Start()
ctx->makeCurrent(old_surface);
// Create master texture (the one sent to the viewer)
master_texture_ = std::make_shared<RenderTexture>();
master_texture_ = std::make_shared<OpenGLTexture>();
master_texture_->Create(ctx, params_.effective_width(), params_.effective_height(), params_.format());
// Create internal FBO for copying textures
copy_buffer_.Create(ctx);
copy_buffer_.Attach(master_texture_);
copy_pipeline_ = olive::ShaderGenerator::DefaultPipeline();
copy_pipeline_ = OpenGLShader::CreateDefault();
cache_frame_load_buffer_.resize(PixelService::GetBufferSize(params_.format(), params_.effective_width(), params_.effective_height()));
@@ -420,22 +419,6 @@ void VideoRendererProcessor::DownloadThreadComplete(const QByteArray &hash)
}
}
VideoRendererThreadBase* VideoRendererProcessor::CurrentThread()
{
return dynamic_cast<VideoRendererThreadBase*>(QThread::currentThread());
}
RenderInstance *VideoRendererProcessor::CurrentInstance()
{
VideoRendererThreadBase* thread = CurrentThread();
if (thread != nullptr) {
return thread->render_instance();
}
return nullptr;
}
RenderTexturePtr VideoRendererProcessor::GetCachedFrame(const rational &time)
{
last_time_requested_ = time;
+3 -10
View File
@@ -27,6 +27,9 @@
#include "node/output/viewer/viewer.h"
#include "render/pixelformat.h"
#include "render/rendermodes.h"
#include "opengl/openglframebuffer.h"
#include "opengl/openglshader.h"
#include "opengl/opengltexture.h"
#include "videorendererdownloadthread.h"
#include "videorendererprocessthread.h"
@@ -84,16 +87,6 @@ public:
*/
bool TryCache(const QByteArray& hash);
/**
* @brief Return current instance of a RenderThread (or nullptr if there is none)
*
* This function attempts a dynamic_cast on QThread::currentThread() to RendererThread, which will return nullptr if
* the cast fails (e.g. if this function is called from the main thread rather than a RendererThread).
*/
static VideoRendererThreadBase* CurrentThread();
static RenderInstance* CurrentInstance();
RenderTexturePtr GetCachedFrame(const rational& time);
void SetViewerNode(ViewerOutput* viewer);
@@ -20,7 +20,7 @@
#include "videorendererprocessthread.h"
#include "videorenderer.h"
#include "videorenderbackend.h"
RendererProcessThread::RendererProcessThread(VideoRendererProcessor* parent,
QOpenGLContext *share_ctx,
@@ -0,0 +1,83 @@
/***
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 <http://www.gnu.org/licenses/>.
***/
#include "videorendererthreadbase.h"
#include <QDebug>
VideoRendererThreadBase::VideoRendererThreadBase(QOpenGLContext *share_ctx, const VideoRenderingParams &params) :
share_ctx_(share_ctx),
render_instance_(params)
{
connect(share_ctx_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Cancel()));
}
RenderInstance *VideoRendererThreadBase::render_instance()
{
return &render_instance_;
}
void VideoRendererThreadBase::run()
{
// Lock mutex for main loop
mutex_.lock();
render_instance_.SetShareContext(share_ctx_);
// Allocate and create resources
bool started = render_instance_.Start();
// Signal that main thread can continue now
WakeCaller();
if (started) {
// Main loop (use Cancel() to exit it)
ProcessLoop();
}
// Free all resources
render_instance_.Stop();
// Unlock mutex before exiting
mutex_.unlock();
}
void VideoRendererThreadBase::WakeCaller()
{
// Signal that main thread can continue now
caller_mutex_.lock();
wait_cond_.wakeAll();
caller_mutex_.unlock();
}
void VideoRendererThreadBase::StartThread(QThread::Priority priority)
{
caller_mutex_.lock();
// Start the thread
QThread::start(priority);
// Wait for thread to finish completion
wait_cond_.wait(&caller_mutex_);
caller_mutex_.unlock();
}
@@ -0,0 +1,68 @@
/***
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 <http://www.gnu.org/licenses/>.
***/
#ifndef RENDERTHREAD_H
#define RENDERTHREAD_H
#include <memory>
#include <QMutex>
#include <QThread>
#include <QWaitCondition>
#include "node/node.h"
#include "render/videoparams.h"
#include "renderinstance.h"
class VideoRendererThreadBase : public QThread
{
Q_OBJECT
public:
VideoRendererThreadBase(QOpenGLContext* share_ctx, const VideoRenderingParams& params);
RenderInstance* render_instance();
void StartThread(Priority priority = InheritPriority);
virtual void run() override;
public slots:
virtual void Cancel() = 0;
protected:
virtual void ProcessLoop() = 0;
QWaitCondition wait_cond_;
QMutex mutex_;
QMutex caller_mutex_;
private:
void WakeCaller();
QOpenGLContext* share_ctx_;
RenderInstance render_instance_;
};
using RendererThreadPtr = std::shared_ptr<VideoRendererThreadBase>;
#endif // RENDERTHREAD_H
+1 -1
View File
@@ -32,6 +32,7 @@
ViewerWidget::ViewerWidget(QWidget *parent) :
QWidget(parent),
video_renderer_(nullptr),
viewer_node_(nullptr),
playback_speed_(0)
{
@@ -161,7 +162,6 @@ void ViewerWidget::ConnectViewerNode(ViewerOutput *node)
}
video_renderer_->SetViewerNode(viewer_node_);
opengl_backend_.SetViewerNode(viewer_node_);
}
void ViewerWidget::DisconnectViewerNode()
+2 -2
View File
@@ -30,7 +30,7 @@
#include "common/rational.h"
#include "node/output/viewer/viewer.h"
#include "render/backend/opengl/openglbackend.h"
#include "render/backend/videorenderbackend.h"
#include "viewerglwidget.h"
#include "viewersizer.h"
#include "widget/playbackcontrols/playbackcontrols.h"
@@ -111,7 +111,7 @@ private:
void PushScrubbedAudio();
// FIXME: Test code only
OpenGLBackend opengl_backend_;
VideoRendererProcessor* video_renderer_;
// End test code
ViewerSizer* sizer_;
+3 -3
View File
@@ -25,8 +25,8 @@
#include <QOpenGLFunctions>
#include <QOpenGLTexture>
#include "render/gl/functions.h"
#include "render/gl/shadergenerators.h"
#include "render/backend/opengl/functions.h"
#include "render/backend/opengl/openglshader.h"
ViewerGLWidget::ViewerGLWidget(QWidget *parent) :
QOpenGLWidget(parent),
@@ -87,7 +87,7 @@ void ViewerGLWidget::paintGL()
void ViewerGLWidget::SetupPipeline()
{
// Re-retrieve pipeline pertaining to this context
pipeline_ = olive::ShaderGenerator::OCIOPipeline(context(),
pipeline_ = OpenGLShader::CreateOCIO(context(),
ocio_lut_,
color_service_->GetProcessor(),
true);