refactoring work to improve issues with new rendering pipeline

This commit is contained in:
itsmattkc
2019-11-04 11:34:43 +11:00
parent 663ae56ac4
commit 97a4294bb3
15 changed files with 408 additions and 253 deletions
+1 -1
View File
@@ -73,7 +73,7 @@ QString OpacityNode::Code(NodeOutput *output)
"uniform float opacity_in;\n"
"\n"
"void main(void) {\n"
" gl_FragColor = tex_in * (opacity_in * 0.01);\n"
" gl_FragColor = texture2D(tex_in, olive_tex_coord) * (opacity_in * 0.01);\n"
"}\n";
}
+1 -1
View File
@@ -64,7 +64,7 @@ QString VideoInput::Code(NodeOutput *output)
"uniform mat4 matrix_in;\n"
"\n"
"void main(void) {\n"
" gl_FragColor = texture2D(olive_tex, vec2(vec4(olive_tex_coord, 0.0, 1.0) * matrix_in));\n"
" gl_FragColor = texture2D(footage_in, vec2(vec4(olive_tex_coord, 0.0, 1.0) * matrix_in));\n"
"}\n";
}
-4
View File
@@ -34,12 +34,8 @@ NodeParam::Type NodeOutput::type()
QVariant NodeOutput::get_realtime_value()
{
parent()->LockProcessing();
QVariant v = parent()->Value(this);
parent()->UnlockProcessing();
return v;
}
+3
View File
@@ -27,6 +27,9 @@ set(OLIVE_SOURCES
render/backend/audiorenderbackend.cpp
render/backend/videorenderbackend.h
render/backend/videorenderbackend.cpp
render/backend/decodercache.h
render/backend/decodercache.cpp
PARENT_SCOPE
)
-2
View File
@@ -16,8 +16,6 @@
set(OLIVE_SOURCES
${OLIVE_SOURCES}
render/backend/opengl/decodercache.h
render/backend/opengl/decodercache.cpp
render/backend/opengl/functions.h
render/backend/opengl/functions.cpp
render/backend/opengl/openglbackend.h
@@ -1,21 +0,0 @@
#include "decodercache.h"
DecoderCache::DecoderCache()
{
}
void DecoderCache::Clear()
{
decoders_.clear();
}
void DecoderCache::AddDecoder(Stream *stream, DecoderPtr shader)
{
decoders_.insert(stream, shader);
}
DecoderPtr DecoderCache::GetDecoder(Stream *stream)
{
return decoders_.value(stream);
}
-26
View File
@@ -1,26 +0,0 @@
#ifndef DECODERCACHE_H
#define DECODERCACHE_H
#include "decoder/decoder.h"
#include "project/item/footage/stream.h"
/**
* @brief Thread-safe cache of decoders
*/
class DecoderCache
{
public:
DecoderCache();
void Clear();
void AddDecoder(Stream* stream, DecoderPtr shader);
DecoderPtr GetDecoder(Stream* stream);
private:
QMap<Stream*, DecoderPtr> decoders_;
};
#endif // DECODERCACHE_H
+123 -48
View File
@@ -7,18 +7,19 @@
OpenGLBackend::OpenGLBackend(QObject *parent) :
VideoRenderBackend(parent),
push_time_(-1)
push_time_(-1),
compiled_(false)
{
}
OpenGLBackend::~OpenGLBackend()
{
Close();
CloseInternal();
}
bool OpenGLBackend::Init()
bool OpenGLBackend::InitInternal()
{
if (!VideoRenderBackend::Init()) {
if (!VideoRenderBackend::InitInternal()) {
return false;
}
@@ -37,6 +38,10 @@ bool OpenGLBackend::Init()
OpenGLWorker* processor = new OpenGLWorker(share_ctx, &shader_cache_, &decoder_cache_);
processor->SetParameters(params());
// Connect to it
connect(processor, SIGNAL(RequestSibling(NodeDependency)), this, SLOT(ThreadRequestedSibling(NodeDependency)));
connect(processor, SIGNAL(CompletedFrame(NodeDependency)), this, SLOT(CompletedFrame(NodeDependency)));
// Finally, we can move it to its own thread
processor->moveToThread(thread);
@@ -61,19 +66,20 @@ bool OpenGLBackend::Init()
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;
if (!compiled_) {
Compile();
}
NodeDependency dep = NodeDependency(viewer_node()->texture_input()->get_connected_output(), time, time);
QMetaObject::invokeMethod(processors_.first(),
"Render",
Qt::QueuedConnection,
Q_ARG(NodeDependency, dep));
}
void OpenGLBackend::CloseInternal()
{
Decompile();
copy_buffer_.Destroy();
@@ -107,10 +113,8 @@ OpenGLTexturePtr OpenGLBackend::GetCachedFrameAsTexture(const rational &time)
return nullptr;
}
bool OpenGLBackend::Compile()
bool OpenGLBackend::CompileInternal()
{
Decompile();
if (viewer_node() == nullptr || !viewer_node()->texture_input()->IsConnected()) {
// Nothing to be done, nothing to compile
return true;
@@ -121,61 +125,65 @@ bool OpenGLBackend::Compile()
if (ret) {
qDebug() << "Compiled successfully!";
compiled_ = true;
} else {
qDebug() << "Compile failed:" << GetError();
Decompile();
}
return ret;
}
void OpenGLBackend::Decompile()
void OpenGLBackend::DecompileInternal()
{
shader_cache_.Clear();
compiled_ = false;
}
bool OpenGLBackend::TraverseCompiling(Node *)
bool OpenGLBackend::TraverseCompiling(Node *n)
{
/*foreach (NodeParam* param, n->parameters()) {
foreach (NodeParam* param, n->parameters()) {
if (param->type() == NodeParam::kInput && param->IsConnected()) {
NodeOutput* connected_output = static_cast<NodeInput*>(param)->get_connected_output();
// Generate the ID we'd use for this shader
QString output_id = GenerateShaderID(connected_output);
// Check if we have a shader or not
if (GetShaderFromID(output_id) == nullptr) {
if (shader_cache_.GetShader(connected_output) == nullptr) {
// Since we don't have a shader, compile one now
QString node_code = connected_output->parent()->Code(connected_output);
// If the node has no code, it mustn't be GPU accelerated
if (!node_code.isEmpty()) {
// Since we have shader code, compile it now
CompiledNode compiled_info;
compiled_info.id = output_id;
OpenGLShaderPtr program;
if (!(compiled_info.program = std::make_shared<OpenGLShader>())) {
SetError("Failed to create OpenGL shader object");
if (!(program = std::make_shared<OpenGLShader>())) {
SetError(QStringLiteral("Failed to create OpenGL shader object"));
return false;
}
if (!compiled_info.program->create()) {
SetError("Failed to create OpenGL shader on device");
if (!program->create()) {
SetError(QStringLiteral("Failed to create OpenGL shader on device"));
return false;
}
if (!compiled_info.program->addShaderFromSourceCode(QOpenGLShader::Fragment, node_code)) {
SetError("Failed to add OpenGL shader code");
if (!program->addShaderFromSourceCode(QOpenGLShader::Fragment, node_code)) {
SetError(QStringLiteral("Failed to add OpenGL fragment shader code"));
return false;
}
if (compiled_info.program->link()) {
SetError("Failed to compile OpenGL shader");
if (!program->addShaderFromSourceCode(QOpenGLShader::Vertex, OpenGLShader::CodeDefaultVertex())) {
SetError(QStringLiteral("Failed to add OpenGL vertex shader code"));
return false;
}
compiled_nodes_.append(compiled_info);
if (!program->link()) {
SetError(QStringLiteral("Failed to compile OpenGL shader: %1").arg(program->log()));
return false;
}
qDebug() << "Compiled" << compiled_info.id;
shader_cache_.AddShader(connected_output, program);
qDebug() << "Compiled" << connected_output->parent()->id() << "->" << connected_output->id();
}
}
@@ -183,11 +191,77 @@ bool OpenGLBackend::TraverseCompiling(Node *)
return false;
}
}
}*/
}
return true;
}
#include <QOpenGLExtraFunctions>
#include <OpenImageIO/imageio.h>
#include "common/define.h"
#include "render/pixelservice.h"
void OpenGLBackend::CompletedFrame(NodeDependency path)
{
caching_ = false;
OpenGLTexturePtr texture = path.node()->get_cached_value(path.range()).value<OpenGLTexturePtr>();
qDebug() << "Retrieved texture for time" << path.in();
qDebug() << "Texture is" << texture.get();
if (texture == nullptr) {
QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions();
QOpenGLExtraFunctions* xf = QOpenGLContext::currentContext()->extraFunctions();
PixelFormatInfo format_info = PixelService::GetPixelFormatInfo(params().format());
QVector<char> data_buffer(PixelService::GetBufferSize(params().format(), params().width(), params().height()));
qDebug() << "Created buffer of size" << data_buffer.size();
// Set up OIIO::ImageSpec for compressing cached images on disk
OIIO::ImageSpec spec(params().width(), params().height(), kRGBAChannels, format_info.oiio_desc);
spec.attribute("compression", "dwaa:200");
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, copy_buffer_.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);
QString cache_fn = CachePathName(QStringLiteral("%1-%2").arg(QString::number(path.in().numerator()), QString::number(path.in().denominator())).toLatin1());
std::string working_fn_std = cache_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();
} else {
qWarning() << "Failed to open output file:" << cache_fn;
}
}
CacheNext();
}
void OpenGLBackend::ThreadCallback(OpenGLTexturePtr texture, const rational& time, const QByteArray& hash)
{
// Threads are all done now, time to proceed
@@ -199,13 +273,10 @@ void OpenGLBackend::ThreadCallback(OpenGLTexturePtr texture, const rational& tim
// We received a texture, time to start downloading it
QString fn = CachePathName(hash);
/*
download_threads_[last_download_thread_%download_threads_.size()]->Queue(texture,
qDebug() << "INSERT DOWNLOAD CODE!";
/*download_threads_[last_download_thread_%download_threads_.size()]->Queue(texture,
fn,
hash);
last_download_thread_++;
*/
hash);*/
} else {
// There was no texture here, we must update the viewer
DownloadThreadComplete(hash);
@@ -244,15 +315,19 @@ void OpenGLBackend::ThreadCallback(OpenGLTexturePtr texture, const rational& tim
CacheNext();
}
void OpenGLBackend::ThreadRequestSibling(NodeDependency dep)
void OpenGLBackend::ThreadRequestedSibling(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)) {
for (int i=1;i<processors_.size();i++) {
if (processors_.at(i)->IsAvailable()) {
QMetaObject::invokeMethod(processors_.at(i),
"RenderAsSibling",
Qt::QueuedConnection,
Q_ARG(NodeDependency, dep));
return;
}*/
}
}
}
+15 -12
View File
@@ -2,7 +2,6 @@
#define OPENGLBACKEND_H
#include "../videorenderbackend.h"
#include "decodercache.h"
#include "openglframebuffer.h"
#include "openglworker.h"
#include "opengltexture.h"
@@ -17,20 +16,19 @@ public:
virtual ~OpenGLBackend() override;
virtual bool Init() override;
virtual void Close() override;
OpenGLTexturePtr GetCachedFrameAsTexture(const rational& time);
public slots:
virtual bool Compile() override;
virtual void Decompile() override;
protected:
virtual bool InitInternal() override;
virtual void CloseInternal() override;
virtual void GenerateFrame(const rational& time) override;
virtual bool CompileInternal() override;
virtual void DecompileInternal() override;
private:
bool TraverseCompiling(Node* n);
@@ -43,12 +41,17 @@ private:
OpenGLShaderPtr copy_pipeline_;
OpenGLShaderCache shader_cache_;
DecoderCache decoder_cache_;
bool compiled_;
private slots:
void CompletedFrame(NodeDependency path);
void ThreadCallback(OpenGLTexturePtr texture, const rational& time, const QByteArray& hash);
void ThreadRequestSibling(NodeDependency dep);
void ThreadRequestedSibling(NodeDependency dep);
void ThreadSkippedFrame(const rational &time, const QByteArray &hash);
+62 -23
View File
@@ -2,6 +2,7 @@
#include <QThread>
#include "functions.h"
#include "node/block/block.h"
#include "node/node.h"
@@ -11,7 +12,8 @@ OpenGLWorker::OpenGLWorker(QOpenGLContext *share_ctx, OpenGLShaderCache *shader_
ctx_(nullptr),
functions_(nullptr),
shader_cache_(shader_cache),
decoder_cache_(decoder_cache)
decoder_cache_(decoder_cache),
working_(0)
{
surface_.create();
}
@@ -48,12 +50,17 @@ void OpenGLWorker::Init()
ctx_->moveToThread(this->thread());
qDebug() << "Processor initialized in thread" << thread() << "- context is in" << ctx_->thread();
//qDebug() << "Processor initialized in thread" << thread() << "- context is in" << ctx_->thread();
// The rest of the initialization needs to occur in the other thread, so we signal for it to start
QMetaObject::invokeMethod(this, "FinishInit", Qt::QueuedConnection);
}
bool OpenGLWorker::IsAvailable()
{
return (working_ == 0);
}
void OpenGLWorker::Close()
{
buffer_.Destroy();
@@ -62,7 +69,7 @@ void OpenGLWorker::Close()
delete ctx_;
}
void OpenGLWorker::Render(const NodeDependency &path)
void OpenGLWorker::Render(NodeDependency path)
{
NodeOutput* output = path.node();
Node* node = output->parent();
@@ -89,6 +96,8 @@ void OpenGLWorker::Render(const NodeDependency &path)
// Now we need the texture done so we call glFinish()
functions_->glFinish();
emit CompletedFrame(path);
}
void OpenGLWorker::UpdateViewportFromParams()
@@ -176,7 +185,7 @@ QList<NodeInput*> OpenGLWorker::ProcessNodeInputsForTime(Node *n, const TimeRang
return connected_inputs;
}
void OpenGLWorker::RunNodeAsShader(Node* node, OpenGLShaderPtr shader)
OpenGLTexturePtr OpenGLWorker::RunNodeAsShader(Node* node, OpenGLShaderPtr shader)
{
shader->bind();
@@ -244,10 +253,19 @@ void OpenGLWorker::RunNodeAsShader(Node* node, OpenGLShaderPtr shader)
}
}
// Attach texture to framebuffer
// Bind framebuffer
// Release framebuffer
// Detach texture
// Create the output texture
OpenGLTexturePtr output = std::make_shared<OpenGLTexture>();
output->Create(ctx_, video_params_.width(), video_params_.height(), video_params_.format());
buffer_.Attach(output);
buffer_.Bind();
olive::gl::Blit(shader);
buffer_.Release();
buffer_.Detach();
// Release any textures we bound before
while (input_texture_count > 0) {
@@ -259,6 +277,8 @@ void OpenGLWorker::RunNodeAsShader(Node* node, OpenGLShaderPtr shader)
}
shader->release();
return output;
}
void OpenGLWorker::FinishInit()
@@ -278,40 +298,54 @@ void OpenGLWorker::FinishInit()
buffer_.Create(ctx_);
qDebug() << "Context in" << ctx_->thread() << "successfully finished";
//qDebug() << "Context in" << ctx_->thread() << "successfully finished";
}
void OpenGLWorker::RenderAsSibling(const NodeDependency &dep)
void OpenGLWorker::RenderAsSibling(NodeDependency dep)
{
NodeOutput* output = dep.node();
Node* node = output->parent();
Node* original_node = output->parent();
Node* node;
rational time = dep.in();
QList<NodeInput*> connected_inputs;
OpenGLShaderPtr shader;
node->LockProcessing();
// Set working state
working_++;
original_node->LockProcessing();
// Firstly we check if this node is a "Block", if it is that means it's part of a linked list of mutually exclusive
// nodes based on time and we might need to locate which Block to attach to
if ((node = ValidateBlock(node, time)) == nullptr) {
if ((node = ValidateBlock(original_node, time)) == nullptr) {
// ValidateBlock() may have returned nullptr if there was no Block found at this time so no texture to return
dep.node()->cache_value(dep.range(), 0);
node->UnlockProcessing();
return;
output->cache_value(dep.range(), 0);
original_node->UnlockProcessing();
goto end_render;
}
// Ensure output is the output matching the node as it may have changed
output = static_cast<NodeOutput*>(node->GetParameterWithID(output->id()));
if (original_node != node) {
// Ensure output is the output matching the node as it may have changed
output = static_cast<NodeOutput*>(node->GetParameterWithID(output->id()));
// Switch locks
original_node->UnlockProcessing();
node->LockProcessing();
}
// Check if the output already has a value for this time
if (output->has_cached_value(dep.range())) {
// If so, we don't need to do anything, we can just send this value and exit here
dep.node()->cache_value(dep.range(), output->get_cached_value(dep.range()));
node->UnlockProcessing();
return;
goto end_render;
}
// We need to run the Node's code to get the correct value for this time
QList<NodeInput*> connected_inputs = ProcessNodeInputsForTime(node, dep.range());
connected_inputs = ProcessNodeInputsForTime(node, dep.range());
// For each connected input, we need to acquire the value from another node
while (!connected_inputs.isEmpty()) {
@@ -368,11 +402,13 @@ void OpenGLWorker::RenderAsSibling(const NodeDependency &dep)
// By this point, the node should have all the inputs it needs to render correctly
// Check if we have a shader for this output
OpenGLShaderPtr shader = shader_cache_->GetShader(output);
shader = shader_cache_->GetShader(output);
if (shader != nullptr) {
// Run code
RunNodeAsShader(node, shader);
OpenGLTexturePtr texture = RunNodeAsShader(node, shader);
output->cache_value(dep.range(), QVariant::fromValue(texture));
} else {
// Generate the value as expected
QVariant value = node->Value(output);
@@ -382,6 +418,9 @@ void OpenGLWorker::RenderAsSibling(const NodeDependency &dep)
}
// We're done!
node->UnlockProcessing();
end_render:
// End this working state
working_--;
}
+14 -5
View File
@@ -5,7 +5,7 @@
#include <QOffscreenSurface>
#include <QOpenGLContext>
#include "decodercache.h"
#include "../decodercache.h"
#include "node/dependency.h"
#include "openglframebuffer.h"
#include "openglshadercache.h"
@@ -47,13 +47,21 @@ public:
*/
void Init();
bool IsAvailable();
public slots:
void Close();
void Render(const NodeDependency& path);
void Render(NodeDependency path);
void RenderAsSibling(NodeDependency dep);
//void Download();
signals:
void RequestSibling(const NodeDependency& path);
void RequestSibling(NodeDependency path);
void CompletedFrame(NodeDependency path);
private:
void ProcessNode();
@@ -64,7 +72,7 @@ private:
QList<NodeInput*> ProcessNodeInputsForTime(Node* n, const TimeRange& time);
void RunNodeAsShader(Node *node, OpenGLShaderPtr shader);
OpenGLTexturePtr RunNodeAsShader(Node *node, OpenGLShaderPtr shader);
QOpenGLContext* share_ctx_;
@@ -81,10 +89,11 @@ private:
DecoderCache* decoder_cache_;
QAtomicInt working_;
private slots:
void FinishInit();
void RenderAsSibling(const NodeDependency& dep);
};
#endif // OPENGLPROCESSOR_H
+109 -1
View File
@@ -1,7 +1,11 @@
#include "renderbackend.h"
#include <QDateTime>
#include <QThread>
RenderBackend::RenderBackend(QObject *parent) :
QObject(parent),
started_(false),
viewer_node_(nullptr)
{
@@ -11,6 +15,50 @@ RenderBackend::~RenderBackend()
{
}
bool RenderBackend::Init()
{
if (started_) {
return true;
}
threads_.resize(QThread::idealThreadCount());
for (int i=0;i<threads_.size();i++) {
QThread* thread = new QThread(this);
threads_.replace(i, thread);
// We use low priority to keep the app responsive at all times (GUI thread should always prioritize over this one)
thread->start(QThread::LowPriority);
}
started_ = InitInternal();
if (!started_) {
Close();
}
return started_;
}
void RenderBackend::Close()
{
if (!started_) {
return;
}
started_ = false;
CloseInternal();
decoder_cache_.Clear();
foreach (QThread* thread, threads_) {
thread->quit();
thread->wait(); // FIXME: Maximum time in case a thread is stuck?
}
threads_.clear();
}
const QString &RenderBackend::GetError() const
{
return error_;
@@ -20,6 +68,8 @@ void RenderBackend::SetViewerNode(ViewerOutput *viewer_node)
{
if (viewer_node_ != nullptr) {
disconnect(viewer_node_, SIGNAL(TextureChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&)));
Decompile();
}
viewer_node_ = viewer_node;
@@ -29,8 +79,61 @@ void RenderBackend::SetViewerNode(ViewerOutput *viewer_node)
}
ViewerNodeChangedEvent(viewer_node_);
}
Decompile();
void RenderBackend::SetCacheName(const QString &s)
{
cache_name_ = s;
cache_time_ = QDateTime::currentMSecsSinceEpoch();
RegenerateCacheID();
}
bool RenderBackend::IsInitiated()
{
return started_;
}
bool RenderBackend::Compile()
{
if (compiled_) {
return true;
}
compiled_ = CompileInternal();
if (!compiled_) {
Decompile();
}
return compiled_;
}
void RenderBackend::Decompile()
{
if (!compiled_) {
return;
}
DecompileInternal();
}
void RenderBackend::RegenerateCacheID()
{
QCryptographicHash hash(QCryptographicHash::Sha1);
if (cache_name_.isEmpty()
|| !cache_time_
|| !GenerateCacheIDInternal(hash)) {
cache_id_.clear();
return;
}
hash.addData(cache_name_.toUtf8());
hash.addData(QString::number(cache_time_).toUtf8());
QByteArray bytes = hash.result();
cache_id_ = bytes.toHex();
}
void RenderBackend::SetError(const QString &error)
@@ -47,3 +150,8 @@ ViewerOutput *RenderBackend::viewer_node() const
{
return viewer_node_;
}
const QVector<QThread *> &RenderBackend::threads()
{
return threads_;
}
+50 -4
View File
@@ -1,6 +1,7 @@
#ifndef RENDERBACKEND_H
#define RENDERBACKEND_H
#include "decodercache.h"
#include "node/output/viewer/viewer.h"
class RenderBackend : public QObject
@@ -12,32 +13,77 @@ public:
Q_DISABLE_COPY_MOVE(RenderBackend)
virtual bool Init() = 0;
bool Init();
virtual void Close() = 0;
void Close();
const QString& GetError() const;
void SetViewerNode(ViewerOutput* viewer_node);
void SetCacheName(const QString& s);
bool IsInitiated();
public slots:
virtual void InvalidateCache(const rational &start_range, const rational &end_range) = 0;
virtual bool Compile() = 0;
bool Compile();
virtual void Decompile() = 0;
void Decompile();
protected:
void RegenerateCacheID();
virtual bool InitInternal() = 0;
virtual void CloseInternal() = 0;
virtual bool CompileInternal() = 0;
virtual void DecompileInternal() = 0;
DecoderCache decoder_cache_;
const QVector<QThread*>& threads();
/**
* @brief Internal function for generating the cache ID
*/
virtual bool GenerateCacheIDInternal(QCryptographicHash& hash) = 0;
void SetError(const QString& error);
virtual void ViewerNodeChangedEvent(ViewerOutput* node);
ViewerOutput* viewer_node() const;
QString cache_name_;
qint64 cache_time_;
QString cache_id_;
private:
/**
* @brief Internal list of RenderProcessThreads
*/
QVector<QThread*> threads_;
/**
* @brief Internal variable that contains whether the Renderer has started or not
*/
bool started_;
/**
* @brief Internal reference to attached viewer node
*/
ViewerOutput* viewer_node_;
/**
* @brief Error string that can be set in SetError() to handle failures
*/
QString error_;
bool compiled_;
};
#endif // RENDERBACKEND_H
+17 -74
View File
@@ -23,11 +23,9 @@
#include <OpenImageIO/imageio.h>
#include <QApplication>
#include <QCryptographicHash>
#include <QDateTime>
#include <QDebug>
#include <QDir>
#include <QtMath>
#include <QThread>
#include "common/filefunctions.h"
#include "opengl/functions.h"
@@ -35,8 +33,7 @@
VideoRenderBackend::VideoRenderBackend(QObject *parent) :
RenderBackend(parent),
caching_(false),
started_(false)
caching_(false)
{
// FIXME: Cache name should actually be the name of the sequence
SetCacheName("Test");
@@ -47,14 +44,6 @@ VideoRenderBackend::~VideoRenderBackend()
Close();
}
void VideoRenderBackend::SetCacheName(const QString &s)
{
cache_name_ = s;
cache_time_ = QDateTime::currentMSecsSinceEpoch();
GenerateCacheIDInternal();
}
void VideoRenderBackend::InvalidateCache(const rational &start_range, const rational &end_range)
{
if (!params_.is_valid()) {
@@ -123,6 +112,17 @@ void VideoRenderBackend::InvalidateCache(const rational &start_range, const rati
CacheNext();
}
bool VideoRenderBackend::InitInternal()
{
cache_frame_load_buffer_.resize(PixelService::GetBufferSize(params_.format(), params_.effective_width(), params_.effective_height()));
return true;
}
void VideoRenderBackend::CloseInternal()
{
cache_frame_load_buffer_.clear();
}
void VideoRenderBackend::ViewerNodeChangedEvent(ViewerOutput *node)
{
if (node != nullptr) {
@@ -131,11 +131,6 @@ void VideoRenderBackend::ViewerNodeChangedEvent(ViewerOutput *node)
}
}
const QVector<QThread *> &VideoRenderBackend::threads()
{
return threads_;
}
const VideoRenderingParams &VideoRenderBackend::params() const
{
return params_;
@@ -151,77 +146,30 @@ void VideoRenderBackend::SetParameters(const VideoRenderingParams& params)
params_ = params;
// Regenerate the cache ID
GenerateCacheIDInternal();
RegenerateCacheID();
}
bool VideoRenderBackend::Init()
{
if (started_) {
return true;
}
threads_.resize(QThread::idealThreadCount());
for (int i=0;i<threads_.size();i++) {
QThread* thread = new QThread(this);
threads_.replace(i, thread);
// We use low priority to keep the app responsive at all times (GUI thread should always prioritize over this one)
thread->start(QThread::LowPriority);
}
cache_frame_load_buffer_.resize(PixelService::GetBufferSize(params_.format(), params_.effective_width(), params_.effective_height()));
started_ = true;
return true;
}
void VideoRenderBackend::Close()
{
if (!started_) {
return;
}
started_ = false;
foreach (QThread* thread, threads_) {
thread->quit();
thread->wait(); // FIXME: Maximum time in case a thread is stuck?
}
threads_.clear();
cache_frame_load_buffer_.clear();
}
void VideoRenderBackend::GenerateCacheIDInternal()
bool VideoRenderBackend::GenerateCacheIDInternal(QCryptographicHash& hash)
{
if (cache_name_.isEmpty() || !params_.is_valid()) {
return;
return false;
}
// Generate an ID that is more or less guaranteed to be unique to this Sequence
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(cache_name_.toUtf8());
hash.addData(QString::number(cache_time_).toUtf8());
hash.addData(QString::number(params_.width()).toUtf8());
hash.addData(QString::number(params_.height()).toUtf8());
hash.addData(QString::number(params_.format()).toUtf8());
hash.addData(QString::number(params_.divider()).toUtf8());
QByteArray bytes = hash.result();
cache_id_ = bytes.toHex();
return true;
}
void VideoRenderBackend::CacheNext()
{
if (cache_queue_.isEmpty() || viewer_node() == nullptr || caching_) {
if (!Init() || cache_queue_.isEmpty() || viewer_node() == nullptr || caching_) {
return;
}
// Make sure cache has started
Init();
rational cache_frame = cache_queue_.takeFirst();
qDebug() << "Caching" << cache_frame.toDouble();
@@ -317,8 +265,3 @@ const char *VideoRenderBackend::GetCachedFrame(const rational &time)
return nullptr;
}
bool VideoRenderBackend::IsStarted()
{
return started_;
}
+13 -31
View File
@@ -45,18 +45,6 @@ public:
virtual ~VideoRenderBackend() override;
void SetCacheName(const QString& s);
/**
* @brief Allocate and start the multithreaded backend
*/
virtual bool Init() override;
/**
* @brief Terminate and deallocate the multithreaded backend
*/
virtual void Close() override;
/**
* @brief Set parameters of the Renderer
*
@@ -92,12 +80,20 @@ public:
*/
bool TryCache(const QByteArray& hash);
bool IsStarted();
public slots:
virtual void InvalidateCache(const rational &start_range, const rational &end_range) override;
protected:
/**
* @brief Allocate and start the multithreaded backend
*/
virtual bool InitInternal() override;
/**
* @brief Terminate and deallocate the multithreaded backend
*/
virtual void CloseInternal() override;
struct HashTimeMapping {
rational time;
QByteArray hash;
@@ -123,8 +119,6 @@ protected:
*/
void CacheNext();
const QVector<QThread*>& threads();
const VideoRenderingParams& params() const;
QMap<rational, QByteArray> time_hash_map_;
@@ -141,28 +135,16 @@ protected:
signals:
void CachedFrameReady(const rational& time);
private:
protected:
/**
* @brief Internal function for generating the cache ID
*/
void GenerateCacheIDInternal();
/**
* @brief Internal list of RenderProcessThreads
*/
QVector<QThread*> threads_;
/**
* @brief Internal variable that contains whether the Renderer has started or not
*/
bool started_;
virtual bool GenerateCacheIDInternal(QCryptographicHash& hash) override;
private:
VideoRenderingParams params_;
QLinkedList<rational> cache_queue_;
QString cache_name_;
qint64 cache_time_;
QString cache_id_;
QByteArray cache_frame_load_buffer_;