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<NodeDependency>();
|
||||
qRegisterMetaType<rational>();
|
||||
qRegisterMetaType<RenderTexturePtr>();
|
||||
qRegisterMetaType<OpenGLTexturePtr>();
|
||||
}
|
||||
|
||||
void Core::StartGUI(bool full_screen)
|
||||
|
||||
@@ -112,11 +112,6 @@ void ViewerPanel::SetTime(const int64_t ×tamp)
|
||||
viewer_->SetTime(timestamp);
|
||||
}
|
||||
|
||||
void ViewerPanel::SetTexture(RenderTexturePtr tex)
|
||||
{
|
||||
viewer_->SetTexture(tex);
|
||||
}
|
||||
|
||||
void ViewerPanel::changeEvent(QEvent *e)
|
||||
{
|
||||
if (e->type() == QEvent::LanguageChange) {
|
||||
|
||||
@@ -63,15 +63,6 @@ public:
|
||||
rational GetTime();
|
||||
|
||||
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);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -25,12 +25,5 @@ set(OLIVE_SOURCES
|
||||
render/backend/audiorenderbackend.cpp
|
||||
render/backend/videorenderbackend.h
|
||||
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
|
||||
)
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
|
||||
#include <QThread>
|
||||
|
||||
OpenGLBackend::OpenGLBackend(QOpenGLContext *share_ctx) :
|
||||
share_ctx_(share_ctx)
|
||||
#include "functions.h"
|
||||
|
||||
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()
|
||||
{
|
||||
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
|
||||
// thread here to prevent QOpenGLWidget trying to reclaim the context before we're done
|
||||
@@ -22,16 +28,14 @@ bool OpenGLBackend::Init()
|
||||
share_ctx_->doneCurrent();
|
||||
|
||||
// Initiate one thread per CPU core
|
||||
for (int i=0;i<threads_.size();i++) {
|
||||
// Instantiate thread
|
||||
QThread* thread = new QThread(this);
|
||||
threads_.replace(i, thread);
|
||||
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
|
||||
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
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// Clear all cores
|
||||
for (int i=0;i<threads_.size();i++) {
|
||||
delete threads_.at(i);
|
||||
copy_buffer_.Destroy();
|
||||
master_texture_ = nullptr;
|
||||
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()
|
||||
@@ -160,6 +205,109 @@ QString OpenGLBackend::GenerateShaderID(NodeOutput *output)
|
||||
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) :
|
||||
QObject(parent),
|
||||
share_ctx_(share_ctx),
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
#include <QOffscreenSurface>
|
||||
#include <QOpenGLShaderProgram>
|
||||
|
||||
#include "../renderbackend.h"
|
||||
#include "../videorenderbackend.h"
|
||||
#include "openglframebuffer.h"
|
||||
#include "opengltexture.h"
|
||||
#include "openglshader.h"
|
||||
|
||||
class OpenGLProcessor : public QObject {
|
||||
public:
|
||||
@@ -45,24 +47,27 @@ private:
|
||||
VideoRenderingParams video_params_;
|
||||
};
|
||||
|
||||
class OpenGLBackend : public RenderBackend
|
||||
class OpenGLBackend : public VideoRenderBackend
|
||||
{
|
||||
public:
|
||||
OpenGLBackend(QOpenGLContext* share_ctx);
|
||||
OpenGLBackend(QOpenGLContext* share_ctx, QObject* parent = nullptr);
|
||||
|
||||
virtual ~OpenGLBackend() override;
|
||||
|
||||
virtual bool Init() override;
|
||||
|
||||
virtual void GenerateFrame(const rational& time) override;
|
||||
|
||||
virtual void Close() override;
|
||||
|
||||
OpenGLTexturePtr GetCachedFrameAsTexture(const rational& time);
|
||||
|
||||
public slots:
|
||||
virtual bool Compile() override;
|
||||
|
||||
virtual void Decompile() override;
|
||||
|
||||
protected:
|
||||
virtual void GenerateFrame(const rational& time) override;
|
||||
|
||||
private:
|
||||
QOpenGLContext* share_ctx_;
|
||||
|
||||
@@ -79,8 +84,22 @@ private:
|
||||
|
||||
QList<CompiledNode> compiled_nodes_;
|
||||
|
||||
QVector<QThread*> threads_;
|
||||
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
|
||||
|
||||
@@ -88,7 +88,7 @@ void OpenGLFramebuffer::Release()
|
||||
context_->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void OpenGLFramebuffer::Attach(RenderTexturePtr texture)
|
||||
void OpenGLFramebuffer::Attach(OpenGLTexturePtr texture)
|
||||
{
|
||||
if (context_ == nullptr) {
|
||||
return;
|
||||
@@ -98,7 +98,7 @@ void OpenGLFramebuffer::Attach(RenderTexturePtr texture)
|
||||
AttachInternal(texture_->texture(), false);
|
||||
}
|
||||
|
||||
void OpenGLFramebuffer::AttachBackBuffer(RenderTexturePtr texture)
|
||||
void OpenGLFramebuffer::AttachBackBuffer(OpenGLTexturePtr texture)
|
||||
{
|
||||
if (context_ == nullptr) {
|
||||
return;
|
||||
|
||||
@@ -42,9 +42,9 @@ public:
|
||||
|
||||
void Release();
|
||||
|
||||
void Attach(RenderTexturePtr texture);
|
||||
void Attach(OpenGLTexturePtr texture);
|
||||
|
||||
void AttachBackBuffer(RenderTexturePtr texture);
|
||||
void AttachBackBuffer(OpenGLTexturePtr texture);
|
||||
|
||||
void Detach();
|
||||
|
||||
@@ -60,7 +60,7 @@ private:
|
||||
|
||||
GLuint buffer_;
|
||||
|
||||
RenderTexturePtr texture_;
|
||||
OpenGLTexturePtr texture_;
|
||||
};
|
||||
|
||||
#endif // OPENGLFRAMEBUFFER_H
|
||||
|
||||
@@ -84,7 +84,7 @@ private:
|
||||
olive::PixelFormat format_;
|
||||
};
|
||||
|
||||
using RenderTexturePtr = std::shared_ptr<OpenGLTexture>;
|
||||
Q_DECLARE_METATYPE(RenderTexturePtr)
|
||||
using OpenGLTexturePtr = std::shared_ptr<OpenGLTexture>;
|
||||
Q_DECLARE_METATYPE(OpenGLTexturePtr)
|
||||
|
||||
#endif // OPENGLTEXTURE_H
|
||||
|
||||
@@ -14,8 +14,6 @@ public:
|
||||
|
||||
virtual bool Init() = 0;
|
||||
|
||||
virtual void GenerateFrame(const rational& time) = 0;
|
||||
|
||||
virtual void Close() = 0;
|
||||
|
||||
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 <QDir>
|
||||
#include <QtMath>
|
||||
#include <QThread>
|
||||
|
||||
#include "common/filefunctions.h"
|
||||
#include "opengl/functions.h"
|
||||
@@ -34,10 +35,8 @@
|
||||
|
||||
VideoRenderBackend::VideoRenderBackend(QObject *parent) :
|
||||
RenderBackend(parent),
|
||||
started_(false),
|
||||
caching_(false),
|
||||
push_time_(-1),
|
||||
starting_(false)
|
||||
started_(false)
|
||||
{
|
||||
// FIXME: Cache name should actually be the name of the sequence
|
||||
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)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
QOpenGLContext* ctx = QOpenGLContext::currentContext();
|
||||
|
||||
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);
|
||||
threads_.resize(QThread::idealThreadCount());
|
||||
|
||||
for (int i=0;i<threads_.size();i++) {
|
||||
threads_[i] = std::make_shared<RendererProcessThread>(this, ctx, params_);
|
||||
threads_[i]->StartThread(QThread::LowPriority);
|
||||
QThread* thread = new QThread(this);
|
||||
threads_.replace(i, thread);
|
||||
|
||||
// Ensure this connection is "Queued" so that it always runs in this object's threaded rather than any of the
|
||||
// other threads
|
||||
connect(threads_.at(i).get(),
|
||||
SIGNAL(RequestSibling(NodeDependency)),
|
||||
this,
|
||||
SLOT(ThreadRequestSibling(NodeDependency)),
|
||||
Qt::QueuedConnection);
|
||||
// We use low priority to keep the app responsive at all times (GUI thread should always prioritize over this one)
|
||||
thread->start(QThread::LowPriority);
|
||||
}
|
||||
|
||||
// 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()));
|
||||
|
||||
started_ = true;
|
||||
@@ -229,20 +185,11 @@ void VideoRenderBackend::Close()
|
||||
|
||||
started_ = false;
|
||||
|
||||
foreach (RendererDownloadThreadPtr download_thread_, download_threads_) {
|
||||
download_thread_->Cancel();
|
||||
}
|
||||
download_threads_.clear();
|
||||
|
||||
foreach (RendererProcessThreadPtr process_thread, threads_) {
|
||||
process_thread->Cancel();
|
||||
foreach (QThread* thread, threads_) {
|
||||
thread->quit();
|
||||
}
|
||||
threads_.clear();
|
||||
|
||||
copy_buffer_.Destroy();
|
||||
master_texture_ = nullptr;
|
||||
copy_pipeline_ = nullptr;
|
||||
|
||||
cache_frame_load_buffer_.clear();
|
||||
}
|
||||
|
||||
@@ -278,7 +225,7 @@ void VideoRenderBackend::CacheNext()
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -329,118 +276,10 @@ bool VideoRenderBackend::TryCache(const QByteArray &hash)
|
||||
return !is_caching;
|
||||
}
|
||||
|
||||
void VideoRenderBackend::ThreadCallback(RenderTexturePtr 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 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)
|
||||
const char *VideoRenderBackend::GetCachedFrame(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_;
|
||||
}
|
||||
}
|
||||
|
||||
if (viewer_node() == nullptr) {
|
||||
// Nothing is connected - nothing to show or render
|
||||
return nullptr;
|
||||
@@ -468,9 +307,7 @@ RenderTexturePtr VideoRenderBackend::GetCachedFrame(const rational &time)
|
||||
|
||||
in->close();
|
||||
|
||||
master_texture_->Upload(cache_frame_load_buffer_.data());
|
||||
|
||||
return master_texture_;
|
||||
return cache_frame_load_buffer_.constData();
|
||||
} else {
|
||||
qWarning() << "OIIO Error:" << OIIO::geterror().c_str();
|
||||
}
|
||||
@@ -479,3 +316,8 @@ RenderTexturePtr VideoRenderBackend::GetCachedFrame(const rational &time)
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool VideoRenderBackend::IsStarted()
|
||||
{
|
||||
return started_;
|
||||
}
|
||||
|
||||
@@ -22,17 +22,11 @@
|
||||
#define VIDEORENDERERBACKEND_H
|
||||
|
||||
#include <QLinkedList>
|
||||
#include <QOpenGLTexture>
|
||||
|
||||
#include "node/output/viewer/viewer.h"
|
||||
#include "renderbackend.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"
|
||||
|
||||
/**
|
||||
* @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()
|
||||
* when the Renderer is about to be destroyed.
|
||||
*/
|
||||
VideoRenderBackend(QObject* parent);
|
||||
VideoRenderBackend(QObject* parent = nullptr);
|
||||
|
||||
virtual ~VideoRenderBackend() override;
|
||||
|
||||
@@ -98,42 +92,22 @@ public:
|
||||
*/
|
||||
bool TryCache(const QByteArray& hash);
|
||||
|
||||
RenderTexturePtr GetCachedFrame(const rational& time);
|
||||
|
||||
virtual void GenerateFrame(const rational&) override {}
|
||||
bool IsStarted();
|
||||
|
||||
public slots:
|
||||
virtual void InvalidateCache(const rational &start_range, const rational &end_range) override;
|
||||
|
||||
virtual bool Compile() override {return true;}
|
||||
|
||||
virtual void Decompile() override {}
|
||||
|
||||
protected:
|
||||
virtual void ViewerNodeChangedEvent(ViewerOutput* node) override;
|
||||
|
||||
signals:
|
||||
void CachedFrameReady(const rational& time);
|
||||
|
||||
private:
|
||||
struct HashTimeMapping {
|
||||
rational time;
|
||||
QByteArray hash;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Internal function for generating the cache ID
|
||||
*/
|
||||
void GenerateCacheIDInternal();
|
||||
virtual void ViewerNodeChangedEvent(ViewerOutput* node) override;
|
||||
|
||||
/**
|
||||
* @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();
|
||||
virtual void GenerateFrame(const rational&) = 0;
|
||||
|
||||
bool ShouldPushTexture(const rational &time);
|
||||
const char *GetCachedFrame(const rational& 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);
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
QVector<RendererProcessThreadPtr> threads_;
|
||||
QVector<QThread*> threads_;
|
||||
|
||||
/**
|
||||
* @brief Internal variable that contains whether the Renderer has started or not
|
||||
@@ -154,42 +159,18 @@ private:
|
||||
|
||||
VideoRenderingParams params_;
|
||||
|
||||
rational last_time_requested_;
|
||||
|
||||
QLinkedList<rational> cache_queue_;
|
||||
QString cache_name_;
|
||||
qint64 cache_time_;
|
||||
QString cache_id_;
|
||||
|
||||
bool caching_;
|
||||
QVector<uchar*> cache_frame_load_buffer_;
|
||||
QByteArray cache_frame_load_buffer_;
|
||||
|
||||
QVector<RendererDownloadThreadPtr> download_threads_;
|
||||
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_;
|
||||
/*QVector<RendererDownloadThreadPtr> download_threads_;
|
||||
int last_download_thread_;*/
|
||||
|
||||
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);
|
||||
|
||||
// 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&)));
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ void ViewerWidget::DisconnectViewerNode()
|
||||
ConnectViewerNode(nullptr);
|
||||
}
|
||||
|
||||
void ViewerWidget::SetTexture(RenderTexturePtr tex)
|
||||
void ViewerWidget::SetTexture(OpenGLTexturePtr tex)
|
||||
{
|
||||
if (tex == nullptr) {
|
||||
gl_widget_->SetTexture(0);
|
||||
@@ -197,7 +197,7 @@ void ViewerWidget::UpdateTextureFromNode(const rational& time)
|
||||
if (viewer_node_ == nullptr) {
|
||||
SetTexture(nullptr);
|
||||
} else {
|
||||
SetTexture(video_renderer_->GetCachedFrame(time));
|
||||
SetTexture(video_renderer_->GetCachedFrameAsTexture(time));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
|
||||
#include "common/rational.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 "viewersizer.h"
|
||||
#include "widget/playbackcontrols/playbackcontrols.h"
|
||||
@@ -73,7 +74,7 @@ public slots:
|
||||
*
|
||||
* @param tex
|
||||
*/
|
||||
void SetTexture(RenderTexturePtr tex);
|
||||
void SetTexture(OpenGLTexturePtr tex);
|
||||
|
||||
void SetTimebase(const rational& r);
|
||||
|
||||
@@ -110,7 +111,7 @@ private:
|
||||
|
||||
void PushScrubbedAudio();
|
||||
|
||||
VideoRenderBackend* video_renderer_;
|
||||
OpenGLBackend* video_renderer_;
|
||||
|
||||
ViewerSizer* sizer_;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user