From 97a4294bb359cc24b123053b5d921e93072f469f Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 4 Nov 2019 11:34:43 +1100 Subject: [PATCH] refactoring work to improve issues with new rendering pipeline --- app/node/color/opacity/opacity.cpp | 2 +- app/node/input/media/video/video.cpp | 2 +- app/node/output.cpp | 4 - app/render/backend/CMakeLists.txt | 3 + app/render/backend/opengl/CMakeLists.txt | 2 - app/render/backend/opengl/decodercache.cpp | 21 --- app/render/backend/opengl/decodercache.h | 26 --- app/render/backend/opengl/openglbackend.cpp | 171 ++++++++++++++------ app/render/backend/opengl/openglbackend.h | 27 ++-- app/render/backend/opengl/openglworker.cpp | 85 +++++++--- app/render/backend/opengl/openglworker.h | 19 ++- app/render/backend/renderbackend.cpp | 110 ++++++++++++- app/render/backend/renderbackend.h | 54 ++++++- app/render/backend/videorenderbackend.cpp | 91 ++--------- app/render/backend/videorenderbackend.h | 44 ++--- 15 files changed, 408 insertions(+), 253 deletions(-) delete mode 100644 app/render/backend/opengl/decodercache.cpp delete mode 100644 app/render/backend/opengl/decodercache.h diff --git a/app/node/color/opacity/opacity.cpp b/app/node/color/opacity/opacity.cpp index 0989defae..8d31d4c5c 100644 --- a/app/node/color/opacity/opacity.cpp +++ b/app/node/color/opacity/opacity.cpp @@ -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"; } diff --git a/app/node/input/media/video/video.cpp b/app/node/input/media/video/video.cpp index 07603db1e..da9b3f5c8 100644 --- a/app/node/input/media/video/video.cpp +++ b/app/node/input/media/video/video.cpp @@ -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"; } diff --git a/app/node/output.cpp b/app/node/output.cpp index 3ef22a09e..5d71542b2 100644 --- a/app/node/output.cpp +++ b/app/node/output.cpp @@ -34,12 +34,8 @@ NodeParam::Type NodeOutput::type() QVariant NodeOutput::get_realtime_value() { - parent()->LockProcessing(); - QVariant v = parent()->Value(this); - parent()->UnlockProcessing(); - return v; } diff --git a/app/render/backend/CMakeLists.txt b/app/render/backend/CMakeLists.txt index 9cc8c3cea..ea717129a 100644 --- a/app/render/backend/CMakeLists.txt +++ b/app/render/backend/CMakeLists.txt @@ -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 ) diff --git a/app/render/backend/opengl/CMakeLists.txt b/app/render/backend/opengl/CMakeLists.txt index ca7564f30..9bc6fd91e 100644 --- a/app/render/backend/opengl/CMakeLists.txt +++ b/app/render/backend/opengl/CMakeLists.txt @@ -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 diff --git a/app/render/backend/opengl/decodercache.cpp b/app/render/backend/opengl/decodercache.cpp deleted file mode 100644 index 29939c289..000000000 --- a/app/render/backend/opengl/decodercache.cpp +++ /dev/null @@ -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); -} diff --git a/app/render/backend/opengl/decodercache.h b/app/render/backend/opengl/decodercache.h deleted file mode 100644 index 47011e965..000000000 --- a/app/render/backend/opengl/decodercache.h +++ /dev/null @@ -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 decoders_; - -}; - -#endif // DECODERCACHE_H diff --git a/app/render/backend/opengl/openglbackend.cpp b/app/render/backend/opengl/openglbackend.cpp index 9744788e9..bf540964b 100644 --- a/app/render/backend/opengl/openglbackend.cpp +++ b/app/render/backend/opengl/openglbackend.cpp @@ -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(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())) { - SetError("Failed to create OpenGL shader object"); + if (!(program = std::make_shared())) { + 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 +#include +#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(); + 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 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 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;iQueue(dep, false, true)) { + for (int i=1;iIsAvailable()) { + QMetaObject::invokeMethod(processors_.at(i), + "RenderAsSibling", + Qt::QueuedConnection, + Q_ARG(NodeDependency, dep)); return; - }*/ + } } } diff --git a/app/render/backend/opengl/openglbackend.h b/app/render/backend/opengl/openglbackend.h index 6a52a4b3c..1ba85187a 100644 --- a/app/render/backend/opengl/openglbackend.h +++ b/app/render/backend/opengl/openglbackend.h @@ -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); diff --git a/app/render/backend/opengl/openglworker.cpp b/app/render/backend/opengl/openglworker.cpp index 9664961a5..259d98f0d 100644 --- a/app/render/backend/opengl/openglworker.cpp +++ b/app/render/backend/opengl/openglworker.cpp @@ -2,6 +2,7 @@ #include +#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 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(); + 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 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(node->GetParameterWithID(output->id())); + if (original_node != node) { + // Ensure output is the output matching the node as it may have changed + output = static_cast(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 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_--; } diff --git a/app/render/backend/opengl/openglworker.h b/app/render/backend/opengl/openglworker.h index 314f47d63..75dc0ec42 100644 --- a/app/render/backend/opengl/openglworker.h +++ b/app/render/backend/opengl/openglworker.h @@ -5,7 +5,7 @@ #include #include -#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 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 diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp index ce3779bad..a20f076f6 100644 --- a/app/render/backend/renderbackend.cpp +++ b/app/render/backend/renderbackend.cpp @@ -1,7 +1,11 @@ #include "renderbackend.h" +#include +#include + 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;istart(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 &RenderBackend::threads() +{ + return threads_; +} diff --git a/app/render/backend/renderbackend.h b/app/render/backend/renderbackend.h index c3bb47e0a..53a3e5165 100644 --- a/app/render/backend/renderbackend.h +++ b/app/render/backend/renderbackend.h @@ -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& 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 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 diff --git a/app/render/backend/videorenderbackend.cpp b/app/render/backend/videorenderbackend.cpp index fa94ddd14..e3e861c01 100644 --- a/app/render/backend/videorenderbackend.cpp +++ b/app/render/backend/videorenderbackend.cpp @@ -23,11 +23,9 @@ #include #include #include -#include #include #include #include -#include #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 &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;istart(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_; -} diff --git a/app/render/backend/videorenderbackend.h b/app/render/backend/videorenderbackend.h index e94185ca4..c4e9eeb09 100644 --- a/app/render/backend/videorenderbackend.h +++ b/app/render/backend/videorenderbackend.h @@ -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& threads(); - const VideoRenderingParams& params() const; QMap 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 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 cache_queue_; - QString cache_name_; - qint64 cache_time_; - QString cache_id_; QByteArray cache_frame_load_buffer_;