separation of previous renderer into base and derivation complete
The previous iteration was fairly OpenGL-heavy. It's now been separated into a base class that is OpenGL independent and a derived class that is OpenGL-based. Over time this should allow for portability away from OpenGL if necessary.
This commit is contained in:
+1
-1
@@ -317,7 +317,7 @@ void Core::DeclareTypesForQt()
|
|||||||
qRegisterMetaType<Task::Status>("Task::Status");
|
qRegisterMetaType<Task::Status>("Task::Status");
|
||||||
qRegisterMetaType<NodeDependency>();
|
qRegisterMetaType<NodeDependency>();
|
||||||
qRegisterMetaType<rational>();
|
qRegisterMetaType<rational>();
|
||||||
qRegisterMetaType<RenderTexturePtr>();
|
qRegisterMetaType<OpenGLTexturePtr>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::StartGUI(bool full_screen)
|
void Core::StartGUI(bool full_screen)
|
||||||
|
|||||||
@@ -112,11 +112,6 @@ void ViewerPanel::SetTime(const int64_t ×tamp)
|
|||||||
viewer_->SetTime(timestamp);
|
viewer_->SetTime(timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewerPanel::SetTexture(RenderTexturePtr tex)
|
|
||||||
{
|
|
||||||
viewer_->SetTexture(tex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ViewerPanel::changeEvent(QEvent *e)
|
void ViewerPanel::changeEvent(QEvent *e)
|
||||||
{
|
{
|
||||||
if (e->type() == QEvent::LanguageChange) {
|
if (e->type() == QEvent::LanguageChange) {
|
||||||
|
|||||||
@@ -63,15 +63,6 @@ public:
|
|||||||
rational GetTime();
|
rational GetTime();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
/**
|
|
||||||
* @brief Set the texture to draw and draw it
|
|
||||||
*
|
|
||||||
* Wrapper function for Viewer::SetTexture().
|
|
||||||
*
|
|
||||||
* @param tex
|
|
||||||
*/
|
|
||||||
void SetTexture(RenderTexturePtr tex);
|
|
||||||
|
|
||||||
void SetTime(const int64_t& timestamp);
|
void SetTime(const int64_t& timestamp);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -25,12 +25,5 @@ set(OLIVE_SOURCES
|
|||||||
render/backend/audiorenderbackend.cpp
|
render/backend/audiorenderbackend.cpp
|
||||||
render/backend/videorenderbackend.h
|
render/backend/videorenderbackend.h
|
||||||
render/backend/videorenderbackend.cpp
|
render/backend/videorenderbackend.cpp
|
||||||
|
|
||||||
# FIXME: Remove these
|
|
||||||
render/backend/videorendererthreadbase.h
|
|
||||||
render/backend/videorendererthreadbase.cpp
|
|
||||||
render/backend/renderinstance.h
|
|
||||||
render/backend/renderinstance.cpp
|
|
||||||
|
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,8 +2,12 @@
|
|||||||
|
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
OpenGLBackend::OpenGLBackend(QOpenGLContext *share_ctx) :
|
#include "functions.h"
|
||||||
share_ctx_(share_ctx)
|
|
||||||
|
OpenGLBackend::OpenGLBackend(QOpenGLContext *share_ctx, QObject *parent) :
|
||||||
|
VideoRenderBackend(parent),
|
||||||
|
share_ctx_(share_ctx),
|
||||||
|
push_time_(-1)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,7 +18,9 @@ OpenGLBackend::~OpenGLBackend()
|
|||||||
|
|
||||||
bool OpenGLBackend::Init()
|
bool OpenGLBackend::Init()
|
||||||
{
|
{
|
||||||
threads_.resize(QThread::idealThreadCount());
|
if (!OpenGLBackend::Init()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Some OpenGL implementations (notably wgl) require the context not to be current before sharing. We block the main
|
// 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
|
// thread here to prevent QOpenGLWidget trying to reclaim the context before we're done
|
||||||
@@ -22,16 +28,14 @@ bool OpenGLBackend::Init()
|
|||||||
share_ctx_->doneCurrent();
|
share_ctx_->doneCurrent();
|
||||||
|
|
||||||
// Initiate one thread per CPU core
|
// Initiate one thread per CPU core
|
||||||
for (int i=0;i<threads_.size();i++) {
|
for (int i=0;i<threads().size();i++) {
|
||||||
// Instantiate thread
|
QThread* thread = threads().at(i);
|
||||||
QThread* thread = new QThread(this);
|
|
||||||
threads_.replace(i, thread);
|
|
||||||
|
|
||||||
// Create one processor object for each thread
|
// Create one processor object for each thread
|
||||||
OpenGLProcessor* processor = new OpenGLProcessor(share_ctx_, thread);
|
OpenGLProcessor* processor = new OpenGLProcessor(share_ctx_, thread);
|
||||||
|
|
||||||
// FIXME: Hardcoded values
|
// FIXME: Hardcoded values
|
||||||
processor->SetParameters(VideoRenderingParams(viewer_node()->video_params(), olive::PIX_FMT_RGBA16F, olive::kOffline));
|
processor->SetParameters(params());
|
||||||
|
|
||||||
// Finally, we can move it to its own thread
|
// Finally, we can move it to its own thread
|
||||||
processor->moveToThread(thread);
|
processor->moveToThread(thread);
|
||||||
@@ -40,23 +44,64 @@ bool OpenGLBackend::Init()
|
|||||||
// We've finished creating shared contexts, we can now restore the context to its previous current state
|
// We've finished creating shared contexts, we can now restore the context to its previous current state
|
||||||
share_ctx_->makeCurrent(old_surface);
|
share_ctx_->makeCurrent(old_surface);
|
||||||
|
|
||||||
|
// 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLBackend::GenerateFrame(const rational &time)
|
void OpenGLBackend::GenerateFrame(const rational &time)
|
||||||
{
|
{
|
||||||
Q_UNUSED(time)
|
Q_UNUSED(time)
|
||||||
|
|
||||||
|
/*threads().first()->Queue(NodeDependency(viewer_node()->texture_input()->get_connected_output(), time, time),
|
||||||
|
true,
|
||||||
|
false);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLBackend::Close()
|
void OpenGLBackend::Close()
|
||||||
{
|
{
|
||||||
|
if (!IsStarted()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Decompile();
|
Decompile();
|
||||||
|
|
||||||
// Clear all cores
|
copy_buffer_.Destroy();
|
||||||
for (int i=0;i<threads_.size();i++) {
|
master_texture_ = nullptr;
|
||||||
delete threads_.at(i);
|
copy_pipeline_ = nullptr;
|
||||||
|
|
||||||
|
OpenGLBackend::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_;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
threads_.clear();
|
|
||||||
|
const char* cached_frame = GetCachedFrame(time);
|
||||||
|
|
||||||
|
if (cached_frame != nullptr) {
|
||||||
|
master_texture_->Upload(cached_frame);
|
||||||
|
|
||||||
|
return master_texture_;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLBackend::Compile()
|
bool OpenGLBackend::Compile()
|
||||||
@@ -160,6 +205,109 @@ QString OpenGLBackend::GenerateShaderID(NodeOutput *output)
|
|||||||
return QString("%1:%2").arg(output->parent()->id(), output->id());
|
return QString("%1:%2").arg(output->parent()->id(), output->id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
OpenGLProcessor::OpenGLProcessor(QOpenGLContext *share_ctx, QObject *parent) :
|
OpenGLProcessor::OpenGLProcessor(QOpenGLContext *share_ctx, QObject *parent) :
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
share_ctx_(share_ctx),
|
share_ctx_(share_ctx),
|
||||||
|
|||||||
@@ -5,8 +5,10 @@
|
|||||||
#include <QOffscreenSurface>
|
#include <QOffscreenSurface>
|
||||||
#include <QOpenGLShaderProgram>
|
#include <QOpenGLShaderProgram>
|
||||||
|
|
||||||
#include "../renderbackend.h"
|
#include "../videorenderbackend.h"
|
||||||
#include "openglframebuffer.h"
|
#include "openglframebuffer.h"
|
||||||
|
#include "opengltexture.h"
|
||||||
|
#include "openglshader.h"
|
||||||
|
|
||||||
class OpenGLProcessor : public QObject {
|
class OpenGLProcessor : public QObject {
|
||||||
public:
|
public:
|
||||||
@@ -45,24 +47,27 @@ private:
|
|||||||
VideoRenderingParams video_params_;
|
VideoRenderingParams video_params_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class OpenGLBackend : public RenderBackend
|
class OpenGLBackend : public VideoRenderBackend
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
OpenGLBackend(QOpenGLContext* share_ctx);
|
OpenGLBackend(QOpenGLContext* share_ctx, QObject* parent = nullptr);
|
||||||
|
|
||||||
virtual ~OpenGLBackend() override;
|
virtual ~OpenGLBackend() override;
|
||||||
|
|
||||||
virtual bool Init() override;
|
virtual bool Init() override;
|
||||||
|
|
||||||
virtual void GenerateFrame(const rational& time) override;
|
|
||||||
|
|
||||||
virtual void Close() override;
|
virtual void Close() override;
|
||||||
|
|
||||||
|
OpenGLTexturePtr GetCachedFrameAsTexture(const rational& time);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual bool Compile() override;
|
virtual bool Compile() override;
|
||||||
|
|
||||||
virtual void Decompile() override;
|
virtual void Decompile() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void GenerateFrame(const rational& time) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QOpenGLContext* share_ctx_;
|
QOpenGLContext* share_ctx_;
|
||||||
|
|
||||||
@@ -79,8 +84,22 @@ private:
|
|||||||
|
|
||||||
QList<CompiledNode> compiled_nodes_;
|
QList<CompiledNode> compiled_nodes_;
|
||||||
|
|
||||||
QVector<QThread*> threads_;
|
|
||||||
QVector<OpenGLProcessor*> processors_;
|
QVector<OpenGLProcessor*> processors_;
|
||||||
|
|
||||||
|
OpenGLTexturePtr master_texture_;
|
||||||
|
rational push_time_;
|
||||||
|
|
||||||
|
OpenGLFramebuffer copy_buffer_;
|
||||||
|
OpenGLShaderPtr copy_pipeline_;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void ThreadCallback(OpenGLTexturePtr texture, const rational& time, const QByteArray& hash);
|
||||||
|
|
||||||
|
void ThreadRequestSibling(NodeDependency dep);
|
||||||
|
|
||||||
|
void ThreadSkippedFrame(const rational &time, const QByteArray &hash);
|
||||||
|
|
||||||
|
void DownloadThreadComplete(const QByteArray &hash);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // OPENGLBACKEND_H
|
#endif // OPENGLBACKEND_H
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ void OpenGLFramebuffer::Release()
|
|||||||
context_->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
context_->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLFramebuffer::Attach(RenderTexturePtr texture)
|
void OpenGLFramebuffer::Attach(OpenGLTexturePtr texture)
|
||||||
{
|
{
|
||||||
if (context_ == nullptr) {
|
if (context_ == nullptr) {
|
||||||
return;
|
return;
|
||||||
@@ -98,7 +98,7 @@ void OpenGLFramebuffer::Attach(RenderTexturePtr texture)
|
|||||||
AttachInternal(texture_->texture(), false);
|
AttachInternal(texture_->texture(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLFramebuffer::AttachBackBuffer(RenderTexturePtr texture)
|
void OpenGLFramebuffer::AttachBackBuffer(OpenGLTexturePtr texture)
|
||||||
{
|
{
|
||||||
if (context_ == nullptr) {
|
if (context_ == nullptr) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ public:
|
|||||||
|
|
||||||
void Release();
|
void Release();
|
||||||
|
|
||||||
void Attach(RenderTexturePtr texture);
|
void Attach(OpenGLTexturePtr texture);
|
||||||
|
|
||||||
void AttachBackBuffer(RenderTexturePtr texture);
|
void AttachBackBuffer(OpenGLTexturePtr texture);
|
||||||
|
|
||||||
void Detach();
|
void Detach();
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ private:
|
|||||||
|
|
||||||
GLuint buffer_;
|
GLuint buffer_;
|
||||||
|
|
||||||
RenderTexturePtr texture_;
|
OpenGLTexturePtr texture_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // OPENGLFRAMEBUFFER_H
|
#endif // OPENGLFRAMEBUFFER_H
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ private:
|
|||||||
olive::PixelFormat format_;
|
olive::PixelFormat format_;
|
||||||
};
|
};
|
||||||
|
|
||||||
using RenderTexturePtr = std::shared_ptr<OpenGLTexture>;
|
using OpenGLTexturePtr = std::shared_ptr<OpenGLTexture>;
|
||||||
Q_DECLARE_METATYPE(RenderTexturePtr)
|
Q_DECLARE_METATYPE(OpenGLTexturePtr)
|
||||||
|
|
||||||
#endif // OPENGLTEXTURE_H
|
#endif // OPENGLTEXTURE_H
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ public:
|
|||||||
|
|
||||||
virtual bool Init() = 0;
|
virtual bool Init() = 0;
|
||||||
|
|
||||||
virtual void GenerateFrame(const rational& time) = 0;
|
|
||||||
|
|
||||||
virtual void Close() = 0;
|
virtual void Close() = 0;
|
||||||
|
|
||||||
const QString& GetError() const;
|
const QString& GetError() const;
|
||||||
|
|||||||
@@ -1,125 +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 <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() << QStringLiteral("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() << QStringLiteral("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_;
|
|
||||||
}
|
|
||||||
@@ -1,80 +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 <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 ¶ms);
|
|
||||||
|
|
||||||
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
|
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QtMath>
|
#include <QtMath>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
#include "common/filefunctions.h"
|
#include "common/filefunctions.h"
|
||||||
#include "opengl/functions.h"
|
#include "opengl/functions.h"
|
||||||
@@ -34,10 +35,8 @@
|
|||||||
|
|
||||||
VideoRenderBackend::VideoRenderBackend(QObject *parent) :
|
VideoRenderBackend::VideoRenderBackend(QObject *parent) :
|
||||||
RenderBackend(parent),
|
RenderBackend(parent),
|
||||||
started_(false),
|
|
||||||
caching_(false),
|
caching_(false),
|
||||||
push_time_(-1),
|
started_(false)
|
||||||
starting_(false)
|
|
||||||
{
|
{
|
||||||
// FIXME: Cache name should actually be the name of the sequence
|
// FIXME: Cache name should actually be the name of the sequence
|
||||||
SetCacheName("Test");
|
SetCacheName("Test");
|
||||||
@@ -132,6 +131,16 @@ void VideoRenderBackend::ViewerNodeChangedEvent(ViewerOutput *node)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QVector<QThread *> &VideoRenderBackend::threads()
|
||||||
|
{
|
||||||
|
return threads_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const VideoRenderingParams &VideoRenderBackend::params() const
|
||||||
|
{
|
||||||
|
return params_;
|
||||||
|
}
|
||||||
|
|
||||||
void VideoRenderBackend::SetParameters(const VideoRenderingParams& params)
|
void VideoRenderBackend::SetParameters(const VideoRenderingParams& params)
|
||||||
{
|
{
|
||||||
// Since we're changing parameters, all the existing threads are invalid and must be removed. They will start again
|
// Since we're changing parameters, all the existing threads are invalid and must be removed. They will start again
|
||||||
@@ -151,69 +160,16 @@ bool VideoRenderBackend::Init()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QOpenGLContext* ctx = QOpenGLContext::currentContext();
|
threads_.resize(QThread::idealThreadCount());
|
||||||
|
|
||||||
int background_thread_count = QThread::idealThreadCount();
|
|
||||||
|
|
||||||
// Some OpenGL implementations (notably wgl) require the context not to be current before sharing
|
|
||||||
QSurface* old_surface = ctx->surface();
|
|
||||||
ctx->doneCurrent();
|
|
||||||
|
|
||||||
threads_.resize(background_thread_count);
|
|
||||||
|
|
||||||
for (int i=0;i<threads_.size();i++) {
|
for (int i=0;i<threads_.size();i++) {
|
||||||
threads_[i] = std::make_shared<RendererProcessThread>(this, ctx, params_);
|
QThread* thread = new QThread(this);
|
||||||
threads_[i]->StartThread(QThread::LowPriority);
|
threads_.replace(i, thread);
|
||||||
|
|
||||||
// Ensure this connection is "Queued" so that it always runs in this object's threaded rather than any of the
|
// We use low priority to keep the app responsive at all times (GUI thread should always prioritize over this one)
|
||||||
// other threads
|
thread->start(QThread::LowPriority);
|
||||||
connect(threads_.at(i).get(),
|
|
||||||
SIGNAL(RequestSibling(NodeDependency)),
|
|
||||||
this,
|
|
||||||
SLOT(ThreadRequestSibling(NodeDependency)),
|
|
||||||
Qt::QueuedConnection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect first thread (master thread) to the callback
|
|
||||||
connect(threads_.first().get(),
|
|
||||||
SIGNAL(CachedFrame(RenderTexturePtr, const rational&, const QByteArray&)),
|
|
||||||
this,
|
|
||||||
SLOT(ThreadCallback(RenderTexturePtr, const rational&, const QByteArray&)),
|
|
||||||
Qt::QueuedConnection);
|
|
||||||
connect(threads_.first().get(),
|
|
||||||
SIGNAL(FrameSkipped(const rational&, const QByteArray&)),
|
|
||||||
this,
|
|
||||||
SLOT(ThreadSkippedFrame(const rational&, const QByteArray&)),
|
|
||||||
Qt::QueuedConnection);
|
|
||||||
|
|
||||||
download_threads_.resize(background_thread_count);
|
|
||||||
|
|
||||||
for (int i=0;i<download_threads_.size();i++) {
|
|
||||||
// Create download thread
|
|
||||||
download_threads_[i] = std::make_shared<VideoRendererDownloadThread>(ctx, params_);
|
|
||||||
download_threads_[i]->StartThread(QThread::LowPriority);
|
|
||||||
|
|
||||||
connect(download_threads_[i].get(),
|
|
||||||
SIGNAL(Downloaded(const QByteArray&)),
|
|
||||||
this,
|
|
||||||
SLOT(DownloadThreadComplete(const QByteArray&)),
|
|
||||||
Qt::QueuedConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
last_download_thread_ = 0;
|
|
||||||
|
|
||||||
// Restore context now that thread creation is complete
|
|
||||||
ctx->makeCurrent(old_surface);
|
|
||||||
|
|
||||||
// Create master texture (the one sent to the viewer)
|
|
||||||
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_ = OpenGLShader::CreateDefault();
|
|
||||||
|
|
||||||
cache_frame_load_buffer_.resize(PixelService::GetBufferSize(params_.format(), params_.effective_width(), params_.effective_height()));
|
cache_frame_load_buffer_.resize(PixelService::GetBufferSize(params_.format(), params_.effective_width(), params_.effective_height()));
|
||||||
|
|
||||||
started_ = true;
|
started_ = true;
|
||||||
@@ -229,20 +185,11 @@ void VideoRenderBackend::Close()
|
|||||||
|
|
||||||
started_ = false;
|
started_ = false;
|
||||||
|
|
||||||
foreach (RendererDownloadThreadPtr download_thread_, download_threads_) {
|
foreach (QThread* thread, threads_) {
|
||||||
download_thread_->Cancel();
|
thread->quit();
|
||||||
}
|
|
||||||
download_threads_.clear();
|
|
||||||
|
|
||||||
foreach (RendererProcessThreadPtr process_thread, threads_) {
|
|
||||||
process_thread->Cancel();
|
|
||||||
}
|
}
|
||||||
threads_.clear();
|
threads_.clear();
|
||||||
|
|
||||||
copy_buffer_.Destroy();
|
|
||||||
master_texture_ = nullptr;
|
|
||||||
copy_pipeline_ = nullptr;
|
|
||||||
|
|
||||||
cache_frame_load_buffer_.clear();
|
cache_frame_load_buffer_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,7 +225,7 @@ void VideoRenderBackend::CacheNext()
|
|||||||
|
|
||||||
qDebug() << "Caching" << cache_frame.toDouble();
|
qDebug() << "Caching" << cache_frame.toDouble();
|
||||||
|
|
||||||
threads_.first()->Queue(NodeDependency(viewer_node()->texture_input()->get_connected_output(), cache_frame, cache_frame), true, false);
|
GenerateFrame(cache_frame);
|
||||||
|
|
||||||
caching_ = true;
|
caching_ = true;
|
||||||
}
|
}
|
||||||
@@ -329,118 +276,10 @@ bool VideoRenderBackend::TryCache(const QByteArray &hash)
|
|||||||
return !is_caching;
|
return !is_caching;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoRenderBackend::ThreadCallback(RenderTexturePtr texture, const rational& time, const QByteArray& hash)
|
const char *VideoRenderBackend::GetCachedFrame(const rational &time)
|
||||||
{
|
|
||||||
// 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 VideoRenderBackend::ThreadRequestSibling(NodeDependency 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 VideoRenderBackend::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 VideoRenderBackend::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--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderTexturePtr VideoRenderBackend::GetCachedFrame(const rational &time)
|
|
||||||
{
|
{
|
||||||
last_time_requested_ = 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_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (viewer_node() == nullptr) {
|
if (viewer_node() == nullptr) {
|
||||||
// Nothing is connected - nothing to show or render
|
// Nothing is connected - nothing to show or render
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -468,9 +307,7 @@ RenderTexturePtr VideoRenderBackend::GetCachedFrame(const rational &time)
|
|||||||
|
|
||||||
in->close();
|
in->close();
|
||||||
|
|
||||||
master_texture_->Upload(cache_frame_load_buffer_.data());
|
return cache_frame_load_buffer_.constData();
|
||||||
|
|
||||||
return master_texture_;
|
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "OIIO Error:" << OIIO::geterror().c_str();
|
qWarning() << "OIIO Error:" << OIIO::geterror().c_str();
|
||||||
}
|
}
|
||||||
@@ -479,3 +316,8 @@ RenderTexturePtr VideoRenderBackend::GetCachedFrame(const rational &time)
|
|||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VideoRenderBackend::IsStarted()
|
||||||
|
{
|
||||||
|
return started_;
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,17 +22,11 @@
|
|||||||
#define VIDEORENDERERBACKEND_H
|
#define VIDEORENDERERBACKEND_H
|
||||||
|
|
||||||
#include <QLinkedList>
|
#include <QLinkedList>
|
||||||
#include <QOpenGLTexture>
|
|
||||||
|
|
||||||
#include "node/output/viewer/viewer.h"
|
#include "node/output/viewer/viewer.h"
|
||||||
#include "renderbackend.h"
|
#include "renderbackend.h"
|
||||||
#include "render/pixelformat.h"
|
#include "render/pixelformat.h"
|
||||||
#include "render/rendermodes.h"
|
#include "render/rendermodes.h"
|
||||||
#include "opengl/openglframebuffer.h"
|
|
||||||
#include "opengl/openglshader.h"
|
|
||||||
#include "opengl/opengltexture.h"
|
|
||||||
#include "videorendererdownloadthread.h"
|
|
||||||
#include "videorendererprocessthread.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A multithreaded OpenGL based renderer for node systems
|
* @brief A multithreaded OpenGL based renderer for node systems
|
||||||
@@ -47,7 +41,7 @@ public:
|
|||||||
* Constructing a Renderer object will not start any threads/backend on its own. Use Start() to do this and Stop()
|
* Constructing a Renderer object will not start any threads/backend on its own. Use Start() to do this and Stop()
|
||||||
* when the Renderer is about to be destroyed.
|
* when the Renderer is about to be destroyed.
|
||||||
*/
|
*/
|
||||||
VideoRenderBackend(QObject* parent);
|
VideoRenderBackend(QObject* parent = nullptr);
|
||||||
|
|
||||||
virtual ~VideoRenderBackend() override;
|
virtual ~VideoRenderBackend() override;
|
||||||
|
|
||||||
@@ -98,42 +92,22 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool TryCache(const QByteArray& hash);
|
bool TryCache(const QByteArray& hash);
|
||||||
|
|
||||||
RenderTexturePtr GetCachedFrame(const rational& time);
|
bool IsStarted();
|
||||||
|
|
||||||
virtual void GenerateFrame(const rational&) override {}
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void InvalidateCache(const rational &start_range, const rational &end_range) override;
|
virtual void InvalidateCache(const rational &start_range, const rational &end_range) override;
|
||||||
|
|
||||||
virtual bool Compile() override {return true;}
|
|
||||||
|
|
||||||
virtual void Decompile() override {}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void ViewerNodeChangedEvent(ViewerOutput* node) override;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void CachedFrameReady(const rational& time);
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct HashTimeMapping {
|
struct HashTimeMapping {
|
||||||
rational time;
|
rational time;
|
||||||
QByteArray hash;
|
QByteArray hash;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
virtual void ViewerNodeChangedEvent(ViewerOutput* node) override;
|
||||||
* @brief Internal function for generating the cache ID
|
|
||||||
*/
|
|
||||||
void GenerateCacheIDInternal();
|
|
||||||
|
|
||||||
/**
|
virtual void GenerateFrame(const rational&) = 0;
|
||||||
* @brief Function called when there are frames in the queue to cache
|
|
||||||
*
|
|
||||||
* This function is NOT thread-safe and should only be called in the main thread.
|
|
||||||
*/
|
|
||||||
void CacheNext();
|
|
||||||
|
|
||||||
bool ShouldPushTexture(const rational &time);
|
const char *GetCachedFrame(const rational& time);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return the path of the cached image at this time
|
* @brief Return the path of the cached image at this time
|
||||||
@@ -142,10 +116,41 @@ private:
|
|||||||
|
|
||||||
void DeferMap(const rational &time, const QByteArray &hash);
|
void DeferMap(const rational &time, const QByteArray &hash);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function called when there are frames in the queue to cache
|
||||||
|
*
|
||||||
|
* This function is NOT thread-safe and should only be called in the main thread.
|
||||||
|
*/
|
||||||
|
void CacheNext();
|
||||||
|
|
||||||
|
const QVector<QThread*>& threads();
|
||||||
|
|
||||||
|
const VideoRenderingParams& params() const;
|
||||||
|
|
||||||
|
QMap<rational, QByteArray> time_hash_map_;
|
||||||
|
|
||||||
|
QList<HashTimeMapping> deferred_maps_;
|
||||||
|
|
||||||
|
QMutex cache_hash_list_mutex_;
|
||||||
|
QVector<QByteArray> cache_hash_list_;
|
||||||
|
|
||||||
|
rational last_time_requested_;
|
||||||
|
|
||||||
|
bool caching_;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void CachedFrameReady(const rational& time);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief Internal function for generating the cache ID
|
||||||
|
*/
|
||||||
|
void GenerateCacheIDInternal();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Internal list of RenderProcessThreads
|
* @brief Internal list of RenderProcessThreads
|
||||||
*/
|
*/
|
||||||
QVector<RendererProcessThreadPtr> threads_;
|
QVector<QThread*> threads_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Internal variable that contains whether the Renderer has started or not
|
* @brief Internal variable that contains whether the Renderer has started or not
|
||||||
@@ -154,42 +159,18 @@ private:
|
|||||||
|
|
||||||
VideoRenderingParams params_;
|
VideoRenderingParams params_;
|
||||||
|
|
||||||
rational last_time_requested_;
|
|
||||||
|
|
||||||
QLinkedList<rational> cache_queue_;
|
QLinkedList<rational> cache_queue_;
|
||||||
QString cache_name_;
|
QString cache_name_;
|
||||||
qint64 cache_time_;
|
qint64 cache_time_;
|
||||||
QString cache_id_;
|
QString cache_id_;
|
||||||
|
|
||||||
bool caching_;
|
QByteArray cache_frame_load_buffer_;
|
||||||
QVector<uchar*> cache_frame_load_buffer_;
|
|
||||||
|
|
||||||
QVector<RendererDownloadThreadPtr> download_threads_;
|
/*QVector<RendererDownloadThreadPtr> download_threads_;
|
||||||
int last_download_thread_;
|
int last_download_thread_;*/
|
||||||
|
|
||||||
RenderTexturePtr master_texture_;
|
|
||||||
rational push_time_;
|
|
||||||
|
|
||||||
OpenGLFramebuffer copy_buffer_;
|
|
||||||
OpenGLShaderPtr copy_pipeline_;
|
|
||||||
|
|
||||||
QMap<rational, QByteArray> time_hash_map_;
|
|
||||||
|
|
||||||
QMutex cache_hash_list_mutex_;
|
|
||||||
QVector<QByteArray> cache_hash_list_;
|
|
||||||
|
|
||||||
QList<HashTimeMapping> deferred_maps_;
|
|
||||||
|
|
||||||
bool starting_;
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void ThreadCallback(RenderTexturePtr texture, const rational& time, const QByteArray& hash);
|
|
||||||
|
|
||||||
void ThreadRequestSibling(NodeDependency dep);
|
|
||||||
|
|
||||||
void ThreadSkippedFrame(const rational &time, const QByteArray &hash);
|
|
||||||
|
|
||||||
void DownloadThreadComplete(const QByteArray &hash);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,188 +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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
***/
|
|
||||||
|
|
||||||
#include "videorendererthreadbase.h"
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QThread>
|
|
||||||
|
|
||||||
#include "common/define.h"
|
|
||||||
#include "videorenderbackend.h"
|
|
||||||
|
|
||||||
VideoRendererThreadBase::VideoRendererThreadBase(VideoRenderBackend* parent, QOpenGLContext *share_ctx, const VideoRenderingParams ¶ms) :
|
|
||||||
parent_(parent),
|
|
||||||
share_ctx_(share_ctx),
|
|
||||||
render_instance_(params)
|
|
||||||
{
|
|
||||||
connect(share_ctx_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Cancel()));
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderInstance *VideoRendererThreadBase::render_instance()
|
|
||||||
{
|
|
||||||
return &render_instance_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VideoRendererThreadBase::Start()
|
|
||||||
{
|
|
||||||
render_instance_.SetShareContext(share_ctx_);
|
|
||||||
|
|
||||||
// Allocate and create resources
|
|
||||||
render_instance_.Start();
|
|
||||||
|
|
||||||
|
|
||||||
// Set up download functions
|
|
||||||
f = render_instance()->context()->functions();
|
|
||||||
xf = render_instance()->context()->extraFunctions();
|
|
||||||
|
|
||||||
f->glGenFramebuffers(1, &read_buffer_);
|
|
||||||
|
|
||||||
int buffer_size = PixelService::GetBufferSize(render_instance()->params().format(),
|
|
||||||
render_instance()->params().width(),
|
|
||||||
render_instance()->params().height());
|
|
||||||
|
|
||||||
data_buffer_.resize(buffer_size);
|
|
||||||
|
|
||||||
format_info_ = PixelService::GetPixelFormatInfo(render_instance()->params().format());
|
|
||||||
|
|
||||||
// Set up OIIO::ImageSpec for compressing cached images on disk
|
|
||||||
spec_ = OIIO::ImageSpec(render_instance()->params().width(), render_instance()->params().height(), kRGBAChannels, format_info_.oiio_desc);
|
|
||||||
spec_.attribute("compression", "dwaa:200");
|
|
||||||
}
|
|
||||||
|
|
||||||
void VideoRendererThreadBase::Stop()
|
|
||||||
{
|
|
||||||
f->glDeleteFramebuffers(1, &read_buffer_);
|
|
||||||
|
|
||||||
// Free all resources
|
|
||||||
render_instance_.Stop();
|
|
||||||
|
|
||||||
thread()->quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VideoRendererThreadBase::Process(const NodeDependency &path, bool sibling)
|
|
||||||
{
|
|
||||||
// Process the Node
|
|
||||||
NodeOutput* output_to_process = path.node();
|
|
||||||
Node* node_to_process = output_to_process->parent();
|
|
||||||
|
|
||||||
texture_ = nullptr;
|
|
||||||
|
|
||||||
QList<Node*> all_deps;
|
|
||||||
bool has_hash = false;
|
|
||||||
bool can_cache = true;
|
|
||||||
|
|
||||||
QByteArray hash;
|
|
||||||
|
|
||||||
if (!sibling) {
|
|
||||||
node_to_process->Lock();
|
|
||||||
|
|
||||||
all_deps = node_to_process->GetDependencies();
|
|
||||||
foreach (Node* dep, all_deps) {
|
|
||||||
dep->Lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check hash
|
|
||||||
QCryptographicHash hasher(QCryptographicHash::Sha1);
|
|
||||||
node_to_process->Hash(&hasher, output_to_process, path.in());
|
|
||||||
hash = hasher.result();
|
|
||||||
|
|
||||||
has_hash = parent_->HasHash(hash);
|
|
||||||
can_cache = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!has_hash){
|
|
||||||
|
|
||||||
if ((can_cache = parent_->TryCache(hash))) {
|
|
||||||
|
|
||||||
QList<NodeDependency> deps = node_to_process->RunDependencies(output_to_process, path.in());
|
|
||||||
|
|
||||||
// Ask for other threads to run these deps while we're here
|
|
||||||
if (!deps.isEmpty()) {
|
|
||||||
for (int i=1;i<deps.size();i++) {
|
|
||||||
emit RequestSibling(deps.at(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the requested value
|
|
||||||
texture_ = output_to_process->get_value(path.in(), path.in()).value<RenderTexturePtr>();
|
|
||||||
|
|
||||||
render_instance()->context()->functions()->glFinish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sibling) {
|
|
||||||
foreach (Node* dep, all_deps) {
|
|
||||||
dep->Unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
node_to_process->Unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (can_cache) {
|
|
||||||
// We cached this frame, signal that it will need to be downloaded to disk
|
|
||||||
emit CachedFrame(texture_, path.in(), hash);
|
|
||||||
} else {
|
|
||||||
// This hash already exists, no need to cache, just map it
|
|
||||||
emit FrameSkipped(path.in(), hash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VideoRendererThreadBase::Download(RenderTexturePtr texture, const QString &fn, const QByteArray &hash)
|
|
||||||
{
|
|
||||||
// Download the texture
|
|
||||||
|
|
||||||
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, read_buffer_);
|
|
||||||
|
|
||||||
xf->glFramebufferTexture2D(GL_READ_FRAMEBUFFER,
|
|
||||||
GL_COLOR_ATTACHMENT0,
|
|
||||||
GL_TEXTURE_2D,
|
|
||||||
texture->texture(),
|
|
||||||
0);
|
|
||||||
|
|
||||||
f->glReadPixels(0,
|
|
||||||
0,
|
|
||||||
texture->width(),
|
|
||||||
texture->height(),
|
|
||||||
format_info_.pixel_format,
|
|
||||||
format_info_.gl_pixel_type,
|
|
||||||
data_buffer_.data());
|
|
||||||
|
|
||||||
xf->glFramebufferTexture2D(GL_READ_FRAMEBUFFER,
|
|
||||||
GL_COLOR_ATTACHMENT0,
|
|
||||||
GL_TEXTURE_2D,
|
|
||||||
0,
|
|
||||||
0);
|
|
||||||
|
|
||||||
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
|
||||||
|
|
||||||
std::string working_fn_std = fn.toStdString();
|
|
||||||
|
|
||||||
std::unique_ptr<OIIO::ImageOutput> out = OIIO::ImageOutput::create(working_fn_std);
|
|
||||||
|
|
||||||
if (out) {
|
|
||||||
out->open(working_fn_std, spec_);
|
|
||||||
out->write_image(format_info_.oiio_desc, data_buffer_.data());
|
|
||||||
out->close();
|
|
||||||
|
|
||||||
emit Downloaded(hash);
|
|
||||||
} else {
|
|
||||||
qWarning() << QStringLiteral("Failed to open output file \"%1\"").arg(fn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,85 +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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
***/
|
|
||||||
|
|
||||||
#ifndef RENDERTHREAD_H
|
|
||||||
#define RENDERTHREAD_H
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <OpenImageIO/imageio.h>
|
|
||||||
|
|
||||||
#include "node/node.h"
|
|
||||||
#include "render/videoparams.h"
|
|
||||||
#include "renderinstance.h"
|
|
||||||
#include "render/pixelservice.h"
|
|
||||||
|
|
||||||
class VideoRenderBackend;
|
|
||||||
|
|
||||||
class VideoRendererThreadBase : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
VideoRendererThreadBase(VideoRenderBackend* parent, QOpenGLContext* share_ctx, const VideoRenderingParams& params);
|
|
||||||
|
|
||||||
RenderInstance* render_instance();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void Start();
|
|
||||||
|
|
||||||
void Stop();
|
|
||||||
|
|
||||||
void Process(const NodeDependency &dep, bool sibling);
|
|
||||||
|
|
||||||
void Download(RenderTexturePtr texture, const QString &fn, const QByteArray &hash);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void RequestSibling(NodeDependency dep);
|
|
||||||
|
|
||||||
void CachedFrame(RenderTexturePtr texture, const rational& time, const QByteArray& hash);
|
|
||||||
|
|
||||||
void FrameSkipped(const rational& time, const QByteArray& hash);
|
|
||||||
|
|
||||||
void Downloaded(const QByteArray& hash);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void WakeCaller();
|
|
||||||
|
|
||||||
VideoRenderBackend* parent_;
|
|
||||||
|
|
||||||
QOpenGLContext* share_ctx_;
|
|
||||||
|
|
||||||
RenderInstance render_instance_;
|
|
||||||
|
|
||||||
RenderTexturePtr texture_;
|
|
||||||
|
|
||||||
GLuint read_buffer_;
|
|
||||||
|
|
||||||
QOpenGLFunctions* f;
|
|
||||||
QOpenGLExtraFunctions* xf;
|
|
||||||
|
|
||||||
PixelFormatInfo format_info_;
|
|
||||||
OIIO::ImageSpec spec_;
|
|
||||||
|
|
||||||
QVector<uchar> data_buffer_;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
using RendererThreadPtr = std::shared_ptr<VideoRendererThreadBase>;
|
|
||||||
|
|
||||||
#endif // RENDERTHREAD_H
|
|
||||||
@@ -84,7 +84,7 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
|||||||
ruler_->SetScale(48.0);
|
ruler_->SetScale(48.0);
|
||||||
|
|
||||||
// Start background renderers
|
// Start background renderers
|
||||||
video_renderer_ = new VideoRenderBackend(this);
|
video_renderer_ = new OpenGLBackend(gl_widget_->context(), this);
|
||||||
connect(video_renderer_, SIGNAL(CachedFrameReady(const rational&)), this, SLOT(RendererCachedFrame(const rational&)));
|
connect(video_renderer_, SIGNAL(CachedFrameReady(const rational&)), this, SLOT(RendererCachedFrame(const rational&)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ void ViewerWidget::DisconnectViewerNode()
|
|||||||
ConnectViewerNode(nullptr);
|
ConnectViewerNode(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewerWidget::SetTexture(RenderTexturePtr tex)
|
void ViewerWidget::SetTexture(OpenGLTexturePtr tex)
|
||||||
{
|
{
|
||||||
if (tex == nullptr) {
|
if (tex == nullptr) {
|
||||||
gl_widget_->SetTexture(0);
|
gl_widget_->SetTexture(0);
|
||||||
@@ -197,7 +197,7 @@ void ViewerWidget::UpdateTextureFromNode(const rational& time)
|
|||||||
if (viewer_node_ == nullptr) {
|
if (viewer_node_ == nullptr) {
|
||||||
SetTexture(nullptr);
|
SetTexture(nullptr);
|
||||||
} else {
|
} else {
|
||||||
SetTexture(video_renderer_->GetCachedFrame(time));
|
SetTexture(video_renderer_->GetCachedFrameAsTexture(time));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,8 @@
|
|||||||
|
|
||||||
#include "common/rational.h"
|
#include "common/rational.h"
|
||||||
#include "node/output/viewer/viewer.h"
|
#include "node/output/viewer/viewer.h"
|
||||||
#include "render/backend/videorenderbackend.h"
|
#include "render/backend/opengl/openglbackend.h"
|
||||||
|
#include "render/backend/opengl/opengltexture.h"
|
||||||
#include "viewerglwidget.h"
|
#include "viewerglwidget.h"
|
||||||
#include "viewersizer.h"
|
#include "viewersizer.h"
|
||||||
#include "widget/playbackcontrols/playbackcontrols.h"
|
#include "widget/playbackcontrols/playbackcontrols.h"
|
||||||
@@ -73,7 +74,7 @@ public slots:
|
|||||||
*
|
*
|
||||||
* @param tex
|
* @param tex
|
||||||
*/
|
*/
|
||||||
void SetTexture(RenderTexturePtr tex);
|
void SetTexture(OpenGLTexturePtr tex);
|
||||||
|
|
||||||
void SetTimebase(const rational& r);
|
void SetTimebase(const rational& r);
|
||||||
|
|
||||||
@@ -110,7 +111,7 @@ private:
|
|||||||
|
|
||||||
void PushScrubbedAudio();
|
void PushScrubbedAudio();
|
||||||
|
|
||||||
VideoRenderBackend* video_renderer_;
|
OpenGLBackend* video_renderer_;
|
||||||
|
|
||||||
ViewerSizer* sizer_;
|
ViewerSizer* sizer_;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user