work towards rewriting the render infrastructure

Mostly foundational work and re-implementing older code in a smarter way
(i.e. context creation code should be slightly faster than it was before)
This commit is contained in:
itsmattkc
2019-11-01 22:58:02 +11:00
parent 429514b1fd
commit c14125e640
12 changed files with 239 additions and 146 deletions
+2
View File
@@ -26,5 +26,7 @@ set(OLIVE_SOURCES
render/backend/opengl/openglshader.cpp
render/backend/opengl/opengltexture.h
render/backend/opengl/opengltexture.cpp
render/backend/opengl/openglworker.h
render/backend/opengl/openglworker.cpp
PARENT_SCOPE
)
+3 -3
View File
@@ -18,8 +18,8 @@
***/
#ifndef GLFUNC_H
#define GLFUNC_H
#ifndef OPENGLFUNCTIONS_H
#define OPENGLFUNCTIONS_H
#include <QMatrix4x4>
@@ -50,4 +50,4 @@ void OCIOBlit(OpenGLShaderPtr pipeline, GLuint lut, bool flipped = false, QMatri
}
}
#endif // GLFUNC_H
#endif // OPENGLFUNCTIONS_H
+21 -92
View File
@@ -1,12 +1,12 @@
#include "openglbackend.h"
#include <QEventLoop>
#include <QThread>
#include "functions.h"
OpenGLBackend::OpenGLBackend(QOpenGLContext *share_ctx, QObject *parent) :
OpenGLBackend::OpenGLBackend(QObject *parent) :
VideoRenderBackend(parent),
share_ctx_(share_ctx),
push_time_(-1)
{
}
@@ -18,38 +18,41 @@ OpenGLBackend::~OpenGLBackend()
bool OpenGLBackend::Init()
{
if (!OpenGLBackend::Init()) {
if (!VideoRenderBackend::Init()) {
return false;
}
// Some OpenGL implementations (notably wgl) require the context not to be current before sharing. We block the main
// thread here to prevent QOpenGLWidget trying to reclaim the context before we're done
QSurface* old_surface = share_ctx_->surface();
share_ctx_->doneCurrent();
QOpenGLContext* share_ctx = QOpenGLContext::currentContext();
if (share_ctx == nullptr) {
qCritical() << "No active OpenGL context to connect to";
return false;
}
// Initiate one thread per CPU core
for (int i=0;i<threads().size();i++) {
QThread* thread = threads().at(i);
// Create one processor object for each thread
OpenGLProcessor* processor = new OpenGLProcessor(share_ctx_, thread);
// FIXME: Hardcoded values
OpenGLWorker* processor = new OpenGLWorker(share_ctx);
processor->SetParameters(params());
// Finally, we can move it to its own thread
processor->moveToThread(thread);
}
// We've finished creating shared contexts, we can now restore the context to its previous current state
share_ctx_->makeCurrent(old_surface);
// Add processor to list
processors_.append(processor);
// This function blocks the main thread intentionally. See the documentation for this function to see why.
processor->Init();
}
// Create master texture (the one sent to the viewer)
master_texture_ = std::make_shared<OpenGLTexture>();
master_texture_->Create(share_ctx_, params().effective_width(), params().effective_height(), params().format());
master_texture_->Create(share_ctx, params().effective_width(), params().effective_height(), params().format());
// Create internal FBO for copying textures
copy_buffer_.Create(share_ctx_);
copy_buffer_.Create(share_ctx);
copy_buffer_.Attach(master_texture_);
copy_pipeline_ = OpenGLShader::CreateDefault();
@@ -77,7 +80,7 @@ void OpenGLBackend::Close()
master_texture_ = nullptr;
copy_pipeline_ = nullptr;
OpenGLBackend::Close();
VideoRenderBackend::Close();
}
OpenGLTexturePtr OpenGLBackend::GetCachedFrameAsTexture(const rational &time)
@@ -127,9 +130,6 @@ bool OpenGLBackend::Compile()
void OpenGLBackend::Decompile()
{
foreach (const CompiledNode& info, compiled_nodes_) {
delete info.program;
}
compiled_nodes_.clear();
}
@@ -153,7 +153,7 @@ bool OpenGLBackend::TraverseCompiling(Node *n)
CompiledNode compiled_info;
compiled_info.id = output_id;
if (!(compiled_info.program = new QOpenGLShaderProgram())) {
if (!(compiled_info.program = std::make_shared<OpenGLShader>())) {
SetError("Failed to create OpenGL shader object");
return false;
}
@@ -188,7 +188,7 @@ bool OpenGLBackend::TraverseCompiling(Node *n)
return true;
}
QOpenGLShaderProgram* OpenGLBackend::GetShaderFromID(const QString &id)
OpenGLShaderPtr OpenGLBackend::GetShaderFromID(const QString &id)
{
foreach (const CompiledNode& info, compiled_nodes_) {
if (info.id == id) {
@@ -307,74 +307,3 @@ void OpenGLBackend::DownloadThreadComplete(const QByteArray &hash)
}
}
}
OpenGLProcessor::OpenGLProcessor(QOpenGLContext *share_ctx, QObject *parent) :
QObject(parent),
share_ctx_(share_ctx),
ctx_(nullptr),
functions_(nullptr)
{
surface_.create();
}
OpenGLProcessor::~OpenGLProcessor()
{
surface_.destroy();
}
bool OpenGLProcessor::IsStarted()
{
return ctx_ != nullptr;
}
void OpenGLProcessor::SetParameters(const VideoRenderingParams &video_params)
{
video_params_ = video_params;
}
void OpenGLProcessor::Init()
{
// Create context object
ctx_ = new QOpenGLContext();
// Set share context
ctx_->setShareContext(share_ctx_);
// Create OpenGL context (automatically destroys any existing if there is one)
if (!ctx_->create()) {
qWarning() << "Failed to create OpenGL context in thread" << thread();
Close();
return;
}
// Make context current on that surface
if (!ctx_->makeCurrent(&surface_)) {
qWarning() << "Failed to makeCurrent() on offscreen surface in thread" << thread();
Close();
return;
}
// Store OpenGL functions instance
functions_ = ctx_->functions();
// Set up OpenGL parameters as necessary
functions_->glEnable(GL_BLEND);
UpdateViewportFromParams();
buffer_.Create(ctx_);
}
void OpenGLProcessor::Close()
{
buffer_.Destroy();
functions_ = nullptr;
delete ctx_;
}
void OpenGLProcessor::UpdateViewportFromParams()
{
if (functions_ != nullptr && video_params_.is_valid()) {
functions_->glViewport(0, 0, video_params_.effective_width(), video_params_.effective_height());
}
}
+6 -47
View File
@@ -1,56 +1,17 @@
#ifndef OPENGLBACKEND_H
#define OPENGLBACKEND_H
#include <memory>
#include <QOffscreenSurface>
#include <QOpenGLShaderProgram>
#include "../videorenderbackend.h"
#include "openglframebuffer.h"
#include "openglworker.h"
#include "opengltexture.h"
#include "openglshader.h"
class OpenGLProcessor : public QObject {
public:
OpenGLProcessor(QOpenGLContext* share_ctx, QObject* parent = nullptr);
virtual ~OpenGLProcessor() override;
Q_DISABLE_COPY_MOVE(OpenGLProcessor)
bool IsStarted();
void SetParameters(const VideoRenderingParams& video_params);
public slots:
void Init();
void Close();
signals:
private:
void UpdateViewportFromParams();
QOpenGLContext* share_ctx_;
QOpenGLContext* ctx_;
QOffscreenSurface surface_;
QOpenGLFunctions* functions_;
OpenGLFramebuffer buffer_;
VideoRenderingParams video_params_;
};
class OpenGLBackend : public VideoRenderBackend
{
Q_OBJECT
public:
OpenGLBackend(QOpenGLContext* share_ctx, QObject* parent = nullptr);
OpenGLBackend(QObject* parent = nullptr);
virtual ~OpenGLBackend() override;
@@ -69,22 +30,20 @@ protected:
virtual void GenerateFrame(const rational& time) override;
private:
QOpenGLContext* share_ctx_;
struct CompiledNode {
QString id;
QOpenGLShaderProgram* program;
OpenGLShaderPtr program;
};
bool TraverseCompiling(Node* n);
QOpenGLShaderProgram *GetShaderFromID(const QString& id);
OpenGLShaderPtr GetShaderFromID(const QString& id);
QString GenerateShaderID(NodeOutput* output);
QList<CompiledNode> compiled_nodes_;
QVector<OpenGLProcessor*> processors_;
QVector<OpenGLWorker*> processors_;
OpenGLTexturePtr master_texture_;
rational push_time_;
+3
View File
@@ -10,6 +10,9 @@ namespace OCIO = OCIO_NAMESPACE::v1;
class OpenGLShader;
using OpenGLShaderPtr = std::shared_ptr<OpenGLShader>;
/**
* @brief A simple QOpenGLShaderProgram derivative with static functions for creating
*/
class OpenGLShader : public QOpenGLShaderProgram {
public:
OpenGLShader();
@@ -26,6 +26,9 @@
#include "render/pixelformat.h"
/**
* @brief A class wrapper around an OpenGL texture
*/
class OpenGLTexture : public QObject
{
Q_OBJECT
+113
View File
@@ -0,0 +1,113 @@
#include "openglworker.h"
#include "node/node.h"
OpenGLWorker::OpenGLWorker(QOpenGLContext *share_ctx, QObject *parent) :
QObject(parent),
share_ctx_(share_ctx),
ctx_(nullptr),
functions_(nullptr)
{
surface_.create();
}
OpenGLWorker::~OpenGLWorker()
{
surface_.destroy();
}
bool OpenGLWorker::IsStarted()
{
return ctx_ != nullptr;
}
void OpenGLWorker::SetParameters(const VideoRenderingParams &video_params)
{
video_params_ = video_params;
}
void OpenGLWorker::Init()
{
// Create context object
ctx_ = new QOpenGLContext();
// Set share context
ctx_->setShareContext(share_ctx_);
// Create OpenGL context (automatically destroys any existing if there is one)
if (!ctx_->create()) {
qWarning() << "Failed to create OpenGL context in thread" << thread();
Close();
return;
}
ctx_->moveToThread(this->thread());
qDebug() << "Processor initialized in thread" << thread() << "- context is in" << ctx_->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);
}
void OpenGLWorker::Close()
{
buffer_.Destroy();
functions_ = nullptr;
delete ctx_;
}
void OpenGLWorker::Render(const NodeDependency &path)
{
NodeOutput* output = path.node();
Node* node = output->parent();
QList<Node*> all_deps = node->GetDependencies();
// Lock all Nodes to prevent UI changes during this render
foreach (Node* dep, all_deps) {
dep->Lock();
}
node->Lock();
// FIXME: Write traversal code
// Start OpenGL flushing now while we do clean up work on the CPU
functions_->glFlush();
// Unlock all Nodes so changes can be made again
foreach (Node* dep, all_deps) {
dep->Unlock();
}
node->Unlock();
// Now we need the texture done so we call glFinish()
functions_->glFinish();
}
void OpenGLWorker::UpdateViewportFromParams()
{
if (functions_ != nullptr && video_params_.is_valid()) {
functions_->glViewport(0, 0, video_params_.effective_width(), video_params_.effective_height());
}
}
void OpenGLWorker::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();
// Set up OpenGL parameters as necessary
functions_->glEnable(GL_BLEND);
UpdateViewportFromParams();
buffer_.Create(ctx_);
qDebug() << "Context in" << ctx_->thread() << "successfully finished";
}
+77
View File
@@ -0,0 +1,77 @@
#ifndef OPENGLPROCESSOR_H
#define OPENGLPROCESSOR_H
#include <QObject>
#include <QOffscreenSurface>
#include <QOpenGLContext>
#include "node/dependency.h"
#include "openglframebuffer.h"
#include "render/videoparams.h"
class OpenGLWorker : public QObject {
Q_OBJECT
public:
OpenGLWorker(QOpenGLContext* share_ctx, QObject* parent = nullptr);
virtual ~OpenGLWorker() override;
Q_DISABLE_COPY_MOVE(OpenGLWorker)
bool IsStarted();
void SetParameters(const VideoRenderingParams& video_params);
/**
* @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).
*/
void Init();
public slots:
void Close();
void Render(const NodeDependency& path);
signals:
private:
void ProcessNode();
void UpdateViewportFromParams();
QOpenGLContext* share_ctx_;
QOpenGLContext* ctx_;
QOffscreenSurface surface_;
QOpenGLFunctions* functions_;
OpenGLFramebuffer buffer_;
VideoRenderingParams video_params_;
private slots:
void FinishInit();
void RenderAsSibling(const NodeDependency& path);
};
#endif // OPENGLPROCESSOR_H
-2
View File
@@ -19,14 +19,12 @@ const QString &RenderBackend::GetError() const
void RenderBackend::SetViewerNode(ViewerOutput *viewer_node)
{
if (viewer_node_ != nullptr) {
disconnect(viewer_node_, SIGNAL(TextureChangedBetween(const rational&, const rational&)), this, SLOT(Compile()));
disconnect(viewer_node_, SIGNAL(TextureChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&)));
}
viewer_node_ = viewer_node;
if (viewer_node_ != nullptr) {
connect(viewer_node_, SIGNAL(TextureChangedBetween(const rational&, const rational&)), this, SLOT(Compile()));
connect(viewer_node_, SIGNAL(TextureChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&)));
}
+9 -1
View File
@@ -1,7 +1,15 @@
#ifndef VULKANBACKEND_H
#define VULKANBACKEND_H
/**
* @brief A Vulkan-based variant of the rendering engine
*
* I literally know nothing about Vulkan but maybe one day I will and can fill this out. Also keep in mind projects
* that can cross-compile GLSL to SPIR like these:
*
* https://github.com/KhronosGroup/glslang
* https://github.com/septag/glslcc
*/
class VulkanBackend
{
public:
+1 -1
View File
@@ -84,7 +84,7 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
ruler_->SetScale(48.0);
// Start background renderers
video_renderer_ = new OpenGLBackend(gl_widget_->context(), this);
video_renderer_ = new OpenGLBackend(this);
connect(video_renderer_, SIGNAL(CachedFrameReady(const rational&)), this, SLOT(RendererCachedFrame(const rational&)));
}
+1
View File
@@ -100,6 +100,7 @@ protected:
* Simple OpenGL drawing function for painting the texture on screen. Standardized around OpenGL ES 3.2 Core.
*/
virtual void paintGL() override;
private:
/**
* @brief Creates the render pipeline shader