137 lines
3.5 KiB
C++
137 lines
3.5 KiB
C++
#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_nodes_in_graph;
|
|
all_nodes_in_graph.append(node);
|
|
all_nodes_in_graph.append(node->GetDependencies());
|
|
|
|
// Lock all Nodes to prevent UI changes during this render
|
|
foreach (Node* dep, all_nodes_in_graph) {
|
|
dep->LockUserInput();
|
|
}
|
|
|
|
// Start traversing graph
|
|
RenderAsSibling(path);
|
|
|
|
// 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_nodes_in_graph) {
|
|
dep->UnlockUserInput();
|
|
}
|
|
|
|
// 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";
|
|
}
|
|
|
|
void OpenGLWorker::RenderAsSibling(const NodeDependency &dep)
|
|
{
|
|
NodeOutput* output = dep.node();
|
|
Node* node = output->parent();
|
|
|
|
node->LockProcessing();
|
|
|
|
// Check if block
|
|
// If yes, traverse previous and next until we find the right block
|
|
// Check all inputs for ones that are dependents
|
|
// If input is NOT connected, set stored value to keyframe at time
|
|
// Some inputs we handle ourselves such as FOOTAGE
|
|
// If input IS connected, we need to traverse down it
|
|
// If more than one input is connected, signal out for a sibling to handle it
|
|
// Keep iterating inputs until all are up to date
|
|
// Finally get value from output
|
|
// If we have shader code, the output is a texture and we handle the I/O
|
|
// If we don't, the output is some other value and the Node will produce the output
|
|
|
|
node->UnlockProcessing();
|
|
}
|