refactoring work to improve issues with new rendering pipeline
This commit is contained in:
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
@@ -34,12 +34,8 @@ NodeParam::Type NodeOutput::type()
|
||||
|
||||
QVariant NodeOutput::get_realtime_value()
|
||||
{
|
||||
parent()->LockProcessing();
|
||||
|
||||
QVariant v = parent()->Value(this);
|
||||
|
||||
parent()->UnlockProcessing();
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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_--;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user