Files
oak-editor/app/render/backend/opengl/openglbackend.cpp
T
itsmattkc 663ae56ac4 more coherent render threading for now
Once again, conceptually this system should work, however it does not seem to
be the most efficient and it wouldn't surprise me if the multithreading was
eventually upgraded to an even more coherent system one day. However for
"core principles" this should be fairly decent.
2019-11-03 04:05:43 +11:00

293 lines
7.0 KiB
C++

#include "openglbackend.h"
#include <QEventLoop>
#include <QThread>
#include "functions.h"
OpenGLBackend::OpenGLBackend(QObject *parent) :
VideoRenderBackend(parent),
push_time_(-1)
{
}
OpenGLBackend::~OpenGLBackend()
{
Close();
}
bool OpenGLBackend::Init()
{
if (!VideoRenderBackend::Init()) {
return false;
}
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
OpenGLWorker* processor = new OpenGLWorker(share_ctx, &shader_cache_, &decoder_cache_);
processor->SetParameters(params());
// Finally, we can move it to its own thread
processor->moveToThread(thread);
// 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());
// Create internal FBO for copying textures
copy_buffer_.Create(share_ctx);
copy_buffer_.Attach(master_texture_);
copy_pipeline_ = OpenGLShader::CreateDefault();
return true;
}
void OpenGLBackend::GenerateFrame(const rational &time)
{
Q_UNUSED(time)
/*threads().first()->Queue(NodeDependency(viewer_node()->texture_input()->get_connected_output(), time, time),
true,
false);*/
}
void OpenGLBackend::Close()
{
if (!IsStarted()) {
return;
}
Decompile();
copy_buffer_.Destroy();
master_texture_ = nullptr;
copy_pipeline_ = nullptr;
VideoRenderBackend::Close();
}
OpenGLTexturePtr OpenGLBackend::GetCachedFrameAsTexture(const rational &time)
{
last_time_requested_ = time;
if (push_time_ >= 0) {
rational temp_push_time = push_time_;
push_time_ = -1;
if (time == temp_push_time) {
return master_texture_;
}
}
const char* cached_frame = GetCachedFrame(time);
if (cached_frame != nullptr) {
master_texture_->Upload(cached_frame);
return master_texture_;
}
return nullptr;
}
bool OpenGLBackend::Compile()
{
Decompile();
if (viewer_node() == nullptr || !viewer_node()->texture_input()->IsConnected()) {
// Nothing to be done, nothing to compile
return true;
}
// Traverse node graph compiling where necessary
bool ret = TraverseCompiling(viewer_node());
if (ret) {
qDebug() << "Compiled successfully!";
} else {
qDebug() << "Compile failed:" << GetError();
}
return ret;
}
void OpenGLBackend::Decompile()
{
shader_cache_.Clear();
}
bool OpenGLBackend::TraverseCompiling(Node *)
{
/*foreach (NodeParam* param, n->parameters()) {
if (param->type() == NodeParam::kInput && param->IsConnected()) {
NodeOutput* connected_output = static_cast<NodeInput*>(param)->get_connected_output();
// Generate the ID we'd use for this shader
QString output_id = GenerateShaderID(connected_output);
// Check if we have a shader or not
if (GetShaderFromID(output_id) == nullptr) {
// Since we don't have a shader, compile one now
QString node_code = connected_output->parent()->Code(connected_output);
// If the node has no code, it mustn't be GPU accelerated
if (!node_code.isEmpty()) {
// Since we have shader code, compile it now
CompiledNode compiled_info;
compiled_info.id = output_id;
if (!(compiled_info.program = std::make_shared<OpenGLShader>())) {
SetError("Failed to create OpenGL shader object");
return false;
}
if (!compiled_info.program->create()) {
SetError("Failed to create OpenGL shader on device");
return false;
}
if (!compiled_info.program->addShaderFromSourceCode(QOpenGLShader::Fragment, node_code)) {
SetError("Failed to add OpenGL shader code");
return false;
}
if (compiled_info.program->link()) {
SetError("Failed to compile OpenGL shader");
return false;
}
compiled_nodes_.append(compiled_info);
qDebug() << "Compiled" << compiled_info.id;
}
}
if (!TraverseCompiling(connected_output->parent())) {
return false;
}
}
}*/
return true;
}
void OpenGLBackend::ThreadCallback(OpenGLTexturePtr texture, const rational& time, const QByteArray& hash)
{
// Threads are all done now, time to proceed
caching_ = false;
DeferMap(time, hash);
if (texture != nullptr) {
// We received a texture, time to start downloading it
QString fn = CachePathName(hash);
/*
download_threads_[last_download_thread_%download_threads_.size()]->Queue(texture,
fn,
hash);
last_download_thread_++;
*/
} else {
// There was no texture here, we must update the viewer
DownloadThreadComplete(hash);
}
// If the connected output is using this time, signal it to update
if (last_time_requested_ == time) {
copy_buffer_.Bind();
QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions();
if (texture == nullptr) {
// No texture, clear the master and push it
f->glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
f->glClear(GL_COLOR_BUFFER_BIT);
} else {
texture->Bind();
f->glViewport(0, 0, master_texture_->width(), master_texture_->height());
olive::gl::Blit(copy_pipeline_);
texture->Release();
}
copy_buffer_.Release();
push_time_ = time;
emit CachedFrameReady(time);
}
CacheNext();
}
void OpenGLBackend::ThreadRequestSibling(NodeDependency dep)
{
Q_UNUSED(dep)
// Try to queue another thread to run this dep in advance
for (int i=1;i<threads().size();i++) {
/*if (threads().at(i)->Queue(dep, false, true)) {
return;
}*/
}
}
void OpenGLBackend::ThreadSkippedFrame(const rational& time, const QByteArray& hash)
{
caching_ = false;
DeferMap(time, hash);
if (!IsCaching(hash)) {
DownloadThreadComplete(hash);
// Signal output to update value
emit CachedFrameReady(time);
}
CacheNext();
}
void OpenGLBackend::DownloadThreadComplete(const QByteArray &hash)
{
cache_hash_list_mutex_.lock();
cache_hash_list_.removeAll(hash);
cache_hash_list_mutex_.unlock();
for (int i=0;i<deferred_maps_.size();i++) {
const HashTimeMapping& deferred = deferred_maps_.at(i);
if (deferred_maps_.at(i).hash == hash) {
// Insert into hash map
time_hash_map_.insert(deferred.time, deferred.hash);
deferred_maps_.removeAt(i);
i--;
}
}
}