made vast improvements to rendering engine and node structure
This was many changes that were largely fundamentally related. They included: - More const modifiers to enforce read only node graphs - Support for fragment and vertex shaders from the nodes - Support for node code loaded externally (embedded into the binary) - Fixed issue preventing two textures from being used in a shader - Removed several unused functions and cleaned up code - Fixed video media node misreading its matrix input
This commit is contained in:
@@ -34,7 +34,8 @@ public:
|
||||
virtual QString id() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual QString Code() const override;
|
||||
virtual bool IsAccelerated() const override;
|
||||
virtual QString CodeFragment() const override;
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(crossdissolve)
|
||||
add_subdirectory(diptoblack)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/block/transition/transition.h
|
||||
|
||||
@@ -65,7 +65,12 @@ void OpacityNode::Retranslate()
|
||||
texture_input_->set_name(tr("Texture"));
|
||||
}
|
||||
|
||||
QString OpacityNode::Code() const
|
||||
bool OpacityNode::IsAccelerated() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
QString OpacityNode::CodeFragment() const
|
||||
{
|
||||
return "#version 110"
|
||||
"\n"
|
||||
|
||||
@@ -39,7 +39,8 @@ public:
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
virtual QString Code() const override;
|
||||
virtual bool IsAccelerated() const override;
|
||||
virtual QString CodeFragment() const override;
|
||||
|
||||
NodeInput* texture_input() const;
|
||||
|
||||
|
||||
@@ -53,7 +53,12 @@ QString SolidGenerator::Description() const
|
||||
return tr("Generate a solid color.");
|
||||
}
|
||||
|
||||
QString SolidGenerator::Code() const
|
||||
bool SolidGenerator::IsAccelerated() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
QString SolidGenerator::CodeFragment() const
|
||||
{
|
||||
return "#version 110\n"
|
||||
"\n"
|
||||
|
||||
@@ -41,7 +41,8 @@ public:
|
||||
virtual QString Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual QString Code() const override;
|
||||
virtual bool IsAccelerated() const override;
|
||||
virtual QString CodeFragment() const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
|
||||
@@ -108,12 +108,3 @@ bool NodeGraph::ContainsNode(Node *n)
|
||||
{
|
||||
return (n->parent() == this);
|
||||
}
|
||||
|
||||
void NodeGraph::Release()
|
||||
{
|
||||
QList<Node*> all_nodes = nodes();
|
||||
|
||||
foreach (Node* n, all_nodes) {
|
||||
n->Release();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,11 +85,6 @@ public:
|
||||
*/
|
||||
bool ContainsNode(Node* n);
|
||||
|
||||
/**
|
||||
* @brief Releases all Nodes in this NodeGraph
|
||||
*/
|
||||
void Release();
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Signal emitted when a Node is added to the graph
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "video.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QMatrix4x4>
|
||||
#include <QOpenGLPixelTransferOptions>
|
||||
|
||||
#include "core.h"
|
||||
@@ -40,28 +41,24 @@ QString VideoInput::Description() const
|
||||
return tr("Import a video footage stream.");
|
||||
}
|
||||
|
||||
void VideoInput::Release()
|
||||
{
|
||||
MediaInput::Release();
|
||||
}
|
||||
|
||||
NodeInput *VideoInput::matrix_input() const
|
||||
{
|
||||
return matrix_input_;
|
||||
}
|
||||
|
||||
QString VideoInput::Code() const
|
||||
bool VideoInput::IsAccelerated() const
|
||||
{
|
||||
return "#version 110\n"
|
||||
"\n"
|
||||
"varying vec2 v_texcoord;\n"
|
||||
"\n"
|
||||
"uniform sampler2D footage_in;\n"
|
||||
"uniform mat4 matrix_in;\n"
|
||||
"\n"
|
||||
"void main(void) {\n"
|
||||
" gl_FragColor = texture2D(footage_in, vec2(vec4(v_texcoord, 0.0, 1.0) * matrix_in));\n"
|
||||
"}\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
QString VideoInput::CodeVertex() const
|
||||
{
|
||||
return ReadFileAsString(":/shaders/videoinput.vert");
|
||||
}
|
||||
|
||||
QString VideoInput::CodeFragment() const
|
||||
{
|
||||
return ReadFileAsString(":/shaders/videoinput.frag");
|
||||
}
|
||||
|
||||
void VideoInput::Retranslate()
|
||||
|
||||
@@ -18,11 +18,11 @@ public:
|
||||
virtual QString Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual void Release() override;
|
||||
|
||||
NodeInput* matrix_input() const;
|
||||
|
||||
virtual QString Code() const override;
|
||||
virtual bool IsAccelerated() const override;
|
||||
virtual QString CodeVertex() const override;
|
||||
virtual QString CodeFragment() const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
|
||||
+39
-9
@@ -21,6 +21,7 @@
|
||||
#include "node.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
|
||||
Node::Node() :
|
||||
can_be_deleted_(true)
|
||||
@@ -58,10 +59,6 @@ QString Node::Description() const
|
||||
return QString();
|
||||
}
|
||||
|
||||
void Node::Release()
|
||||
{
|
||||
}
|
||||
|
||||
void Node::Retranslate()
|
||||
{
|
||||
}
|
||||
@@ -153,6 +150,18 @@ void Node::DependentEdgeChanged(NodeInput *from)
|
||||
}
|
||||
}
|
||||
|
||||
QString Node::ReadFileAsString(const QString &filename)
|
||||
{
|
||||
QFile f(filename);
|
||||
QString file_data;
|
||||
if (f.open(QFile::ReadOnly | QFile::Text)) {
|
||||
QTextStream text_stream(&f);
|
||||
file_data = text_stream.readAll();
|
||||
f.close();
|
||||
}
|
||||
return file_data;
|
||||
}
|
||||
|
||||
void Node::LockUserInput()
|
||||
{
|
||||
user_input_lock_.lock();
|
||||
@@ -179,11 +188,9 @@ void Node::CopyInputs(Node *source, Node *destination, bool include_connections)
|
||||
if (p->type() == NodeParam::kInput) {
|
||||
NodeInput* src = static_cast<NodeInput*>(p);
|
||||
|
||||
if (src->dependent()) {
|
||||
NodeInput* dst = static_cast<NodeInput*>(dst_param.at(i));
|
||||
NodeInput* dst = static_cast<NodeInput*>(dst_param.at(i));
|
||||
|
||||
NodeInput::CopyValues(src, dst, include_connections, false);
|
||||
}
|
||||
NodeInput::CopyValues(src, dst, include_connections, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,7 +366,17 @@ QList<Node *> Node::GetImmediateDependencies() const
|
||||
return node_list;
|
||||
}
|
||||
|
||||
QString Node::Code() const
|
||||
bool Node::IsAccelerated() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QString Node::CodeVertex() const
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString Node::CodeFragment() const
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
@@ -442,6 +459,19 @@ NodeOutput *Node::output() const
|
||||
return output_;
|
||||
}
|
||||
|
||||
QVariant Node::InputValueFromTable(NodeInput *input, const NodeValueTable &table) const
|
||||
{
|
||||
NodeParam::DataType find_data_type = input->data_type();
|
||||
|
||||
// Exception for Footage types (try to get a Texture instead)
|
||||
if (find_data_type == NodeParam::kFootage) {
|
||||
find_data_type = NodeParam::kTexture;
|
||||
}
|
||||
|
||||
// Try to get a value from it
|
||||
return table.Get(find_data_type);
|
||||
}
|
||||
|
||||
void Node::AddInput(NodeInput *input)
|
||||
{
|
||||
AddParameter(input);
|
||||
|
||||
+13
-7
@@ -97,11 +97,6 @@ public:
|
||||
*/
|
||||
virtual QString Description() const;
|
||||
|
||||
/**
|
||||
* @brief Signals the Node that it won't be used for a while and can deallocate some memory
|
||||
*/
|
||||
virtual void Release();
|
||||
|
||||
/**
|
||||
* @brief Function called to retranslate parameter names (should be overridden in derivatives)
|
||||
*/
|
||||
@@ -136,10 +131,17 @@ public:
|
||||
*/
|
||||
QList<Node*> GetImmediateDependencies() const;
|
||||
|
||||
virtual bool IsAccelerated() const;
|
||||
|
||||
/**
|
||||
* @brief Generate OpenCL hardware accelerated code for this Node
|
||||
* @brief Generate hardware accelerated code for this Node
|
||||
*/
|
||||
virtual QString Code() const;
|
||||
virtual QString CodeVertex() const;
|
||||
|
||||
/**
|
||||
* @brief Generate hardware accelerated code for this Node
|
||||
*/
|
||||
virtual QString CodeFragment() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the parameter with the specified ID (or nullptr if it doesn't exist)
|
||||
@@ -266,6 +268,8 @@ public:
|
||||
|
||||
NodeOutput* output() const;
|
||||
|
||||
virtual QVariant InputValueFromTable(NodeInput* input, const NodeValueTable& table) const;
|
||||
|
||||
protected:
|
||||
void AddInput(NodeInput* input);
|
||||
|
||||
@@ -275,6 +279,8 @@ protected:
|
||||
|
||||
virtual void DependentEdgeChanged(NodeInput* from);
|
||||
|
||||
static QString ReadFileAsString(const QString& filename);
|
||||
|
||||
public slots:
|
||||
|
||||
signals:
|
||||
|
||||
@@ -102,16 +102,31 @@ TrackOutput* TrackList::AddTrack()
|
||||
NodeParam::ConnectEdge(track->output(), assoc_input);
|
||||
|
||||
// FIXME: Test code only
|
||||
/*if (track_input_->GetSize() > 1) {
|
||||
if (current_last_track->output()->IsConnected()) {
|
||||
AlphaOverBlend* blend = new AlphaOverBlend();
|
||||
GetParentGraph()->AddNode(blend);
|
||||
if (track_input_->GetSize() > 1) {
|
||||
TrackOutput* last_track = nullptr;
|
||||
|
||||
NodeParam::ConnectEdge(track->output(), blend->blend_input());
|
||||
NodeParam::ConnectEdge(current_last_track->output(), blend->base_input());
|
||||
NodeParam::ConnectEdge(blend->output(), current_last_track->output()->edges().first()->input());
|
||||
for (int i=track_cache_.size()-1;i>=0;i--) {
|
||||
TrackOutput* test_track = track_cache_.at(i);
|
||||
|
||||
if (test_track && test_track != track) {
|
||||
last_track = test_track;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
if (last_track && last_track->output()->IsConnected()) {
|
||||
foreach (NodeEdgePtr edge, last_track->output()->edges()) {
|
||||
if (edge->input()->parentNode() != track_input_->parentNode()) {
|
||||
AlphaOverBlend* blend = new AlphaOverBlend();
|
||||
GetParentGraph()->AddNode(blend);
|
||||
|
||||
NodeParam::ConnectEdge(track->output(), blend->blend_input());
|
||||
NodeParam::ConnectEdge(last_track->output(), blend->base_input());
|
||||
NodeParam::ConnectEdge(blend->output(), edge->input());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// End test code
|
||||
|
||||
return track;
|
||||
|
||||
@@ -70,6 +70,7 @@ void Sequence::add_default_nodes()
|
||||
Node* audio_track_output = timeline_output_->track_list(TrackType::kTrackTypeAudio)->AddTrack();
|
||||
NodeParam::ConnectEdge(video_track_output->output(), viewer_output_->texture_input());
|
||||
NodeParam::ConnectEdge(audio_track_output->output(), viewer_output_->samples_input());
|
||||
//timeline_output_->track_list(TrackType::kTrackTypeVideo)->AddTrack();
|
||||
|
||||
// Update the timebase on these nodes
|
||||
set_video_params(video_params_);
|
||||
|
||||
@@ -32,7 +32,7 @@ void AudioRenderBackend::InvalidateCache(const rational &start_range, const rati
|
||||
RenderBackend::InvalidateCache(start_range, end_range);
|
||||
|
||||
rational start_range_adj = qMax(rational(0), start_range);
|
||||
rational end_range_adj = qMin(SequenceLength(), end_range);
|
||||
rational end_range_adj = qMin(GetSequenceLength(), end_range);
|
||||
|
||||
// Add the range to the list
|
||||
cache_queue_.append(TimeRange(start_range_adj, end_range_adj));
|
||||
@@ -41,7 +41,7 @@ void AudioRenderBackend::InvalidateCache(const rational &start_range, const rati
|
||||
ValidateRanges();
|
||||
|
||||
// Queue value update
|
||||
QueueValueUpdate(TimeRange(start_range, end_range));
|
||||
QueueValueUpdate();
|
||||
|
||||
// Start caching cycle if it hasn't started already
|
||||
CacheNext();
|
||||
|
||||
@@ -28,7 +28,7 @@ FramePtr AudioRenderWorker::RetrieveFromDecoder(DecoderPtr decoder, const TimeRa
|
||||
return decoder->RetrieveAudio(range.in(), range.out() - range.in(), audio_params_);
|
||||
}
|
||||
|
||||
NodeValueTable AudioRenderWorker::RenderBlock(TrackOutput *track, const TimeRange &range)
|
||||
NodeValueTable AudioRenderWorker::RenderBlock(const TrackOutput *track, const TimeRange &range)
|
||||
{
|
||||
QList<Block*> active_blocks = track->BlocksAtTimeRange(range);
|
||||
|
||||
@@ -42,8 +42,8 @@ NodeValueTable AudioRenderWorker::RenderBlock(TrackOutput *track, const TimeRang
|
||||
TimeRange range_for_block(qMax(b->in(), range.in()),
|
||||
qMin(b->out(), range.out()));
|
||||
|
||||
NodeValueTable table = RenderAsSibling(NodeDependency(b,
|
||||
range_for_block));
|
||||
NodeValueTable table = ProcessNode(NodeDependency(b,
|
||||
range_for_block));
|
||||
|
||||
QByteArray samples_from_this_block = table.Take(NodeParam::kSamples).toByteArray();
|
||||
int destination_offset = audio_params_.time_to_bytes(range_for_block.in() - range.in());
|
||||
|
||||
@@ -18,7 +18,7 @@ protected:
|
||||
|
||||
virtual FramePtr RetrieveFromDecoder(DecoderPtr decoder, const TimeRange& range) override;
|
||||
|
||||
virtual NodeValueTable RenderBlock(TrackOutput *track, const TimeRange& range) override;
|
||||
virtual NodeValueTable RenderBlock(const TrackOutput *track, const TimeRange& range) override;
|
||||
|
||||
private:
|
||||
AudioRenderingParams audio_params_;
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
#include "functions.h"
|
||||
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QOpenGLExtraFunctions>
|
||||
#include <QOpenGLVertexArrayObject>
|
||||
#include <QOpenGLBuffer>
|
||||
@@ -64,7 +63,7 @@ const GLfloat flipped_blit_texcoords[] = {
|
||||
*
|
||||
* Currently active QOpenGLFunctions object (use context()->functions() if unsure).
|
||||
*/
|
||||
void PrepareToDraw(QOpenGLFunctions* f) {
|
||||
void olive::gl::PrepareToDraw(QOpenGLFunctions* f) {
|
||||
f->glGenerateMipmap(GL_TEXTURE_2D);
|
||||
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
@@ -96,8 +95,8 @@ void olive::gl::Blit(OpenGLShaderPtr pipeline, bool flipped, QMatrix4x4 matrix)
|
||||
|
||||
pipeline->bind();
|
||||
|
||||
pipeline->setUniformValue("mvp_matrix", matrix);
|
||||
pipeline->setUniformValue("texture", 0);
|
||||
pipeline->setUniformValue("ove_mvpmat", matrix);
|
||||
pipeline->setUniformValue("ove_maintex", 0);
|
||||
|
||||
GLuint vertex_location = static_cast<GLuint>(pipeline->attributeLocation("a_position"));
|
||||
m_vbo.bind();
|
||||
@@ -135,7 +134,7 @@ void olive::gl::OCIOBlit(OpenGLShaderPtr pipeline,
|
||||
|
||||
pipeline->bind();
|
||||
|
||||
pipeline->setUniformValue("tex2", 2);
|
||||
pipeline->setUniformValue("ove_ociolut", 2);
|
||||
|
||||
olive::gl::Blit(pipeline, flipped, matrix);
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#define OPENGLFUNCTIONS_H
|
||||
|
||||
#include <QMatrix4x4>
|
||||
#include <QOpenGLFunctions>
|
||||
|
||||
#include "openglshader.h"
|
||||
|
||||
@@ -47,6 +48,8 @@ void Blit(OpenGLShaderPtr pipeline, bool flipped = false, QMatrix4x4 matrix = QM
|
||||
|
||||
void OCIOBlit(OpenGLShaderPtr pipeline, GLuint lut, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4());
|
||||
|
||||
void PrepareToDraw(QOpenGLFunctions* f);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,8 +46,6 @@ bool OpenGLBackend::InitInternal()
|
||||
|
||||
void OpenGLBackend::CloseInternal()
|
||||
{
|
||||
Decompile();
|
||||
|
||||
master_texture_ = nullptr;
|
||||
}
|
||||
|
||||
@@ -79,16 +77,26 @@ bool OpenGLBackend::CompileInternal()
|
||||
// Check if we have a shader or not
|
||||
if (!shader_cache_.Has(n->id())) {
|
||||
// Since we don't have a shader, compile one now
|
||||
QString node_code = n->Code();
|
||||
|
||||
// If the node has no code, it mustn't be GPU accelerated
|
||||
if (node_code.isEmpty()) {
|
||||
if (!n->IsAccelerated()) {
|
||||
// We enter a null shader so we don't try to compile this again
|
||||
shader_cache_.Add(n->id(), nullptr);
|
||||
} else {
|
||||
// Since we have shader code, compile it now
|
||||
OpenGLShaderPtr program;
|
||||
|
||||
QString frag_code = n->CodeFragment();
|
||||
QString vert_code = n->CodeVertex();
|
||||
|
||||
if (frag_code.isEmpty()) {
|
||||
frag_code = OpenGLShader::CodeDefaultFragment();
|
||||
}
|
||||
|
||||
if (vert_code.isEmpty()) {
|
||||
vert_code = OpenGLShader::CodeDefaultVertex();
|
||||
}
|
||||
|
||||
if (!(program = std::make_shared<OpenGLShader>())) {
|
||||
SetError(QStringLiteral("Failed to create OpenGL shader object"));
|
||||
return false;
|
||||
@@ -99,12 +107,12 @@ bool OpenGLBackend::CompileInternal()
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!program->addShaderFromSourceCode(QOpenGLShader::Fragment, node_code)) {
|
||||
if (!program->addShaderFromSourceCode(QOpenGLShader::Fragment, frag_code)) {
|
||||
SetError(QStringLiteral("Failed to add OpenGL fragment shader code"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!program->addShaderFromSourceCode(QOpenGLShader::Vertex, OpenGLShader::CodeDefaultVertex())) {
|
||||
if (!program->addShaderFromSourceCode(QOpenGLShader::Vertex, vert_code)) {
|
||||
SetError(QStringLiteral("Failed to add OpenGL vertex shader code"));
|
||||
return false;
|
||||
}
|
||||
@@ -115,8 +123,6 @@ bool OpenGLBackend::CompileInternal()
|
||||
}
|
||||
|
||||
shader_cache_.Add(n->id(), program);
|
||||
|
||||
//qDebug() << "Compiled" << connected_output->parent()->id() << "->" << connected_output->id();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ OpenGLShaderPtr OpenGLShader::CreateOCIO(QOpenGLContext* ctx,
|
||||
OCIO::ConstProcessorRcPtr processor,
|
||||
bool alpha_is_associated)
|
||||
{
|
||||
|
||||
QOpenGLExtraFunctions* xf = ctx->extraFunctions();
|
||||
|
||||
// Create LUT texture
|
||||
@@ -94,9 +93,9 @@ OpenGLShaderPtr OpenGLShader::CreateOCIO(QOpenGLContext* ctx,
|
||||
shader_text.append(CodeAlphaReassociate(reassociate_func_name));
|
||||
|
||||
// Make OCIO call pass through disassociate and reassociate function
|
||||
shader_call = QString("%3(%1(%2(col), tex2));").arg(ocio_func_name,
|
||||
disassociate_func_name,
|
||||
reassociate_func_name);
|
||||
shader_call = QStringLiteral("%3(%1(%2(col), ove_ociolut));").arg(ocio_func_name,
|
||||
disassociate_func_name,
|
||||
reassociate_func_name);
|
||||
|
||||
} else {
|
||||
|
||||
@@ -107,18 +106,18 @@ OpenGLShaderPtr OpenGLShader::CreateOCIO(QOpenGLContext* ctx,
|
||||
shader_text.append(CodeAlphaAssociate(associate_func_name));
|
||||
|
||||
// Make OCIO call pass through associate function
|
||||
shader_call = QString("%2(%1(col, tex2));").arg(ocio_func_name, associate_func_name);
|
||||
shader_call = QStringLiteral("%2(%1(col, ove_ociolut));").arg(ocio_func_name, associate_func_name);
|
||||
|
||||
}
|
||||
|
||||
// Add process() function, which GetPipeline() will call if specified
|
||||
QString process_function_name = "process";
|
||||
shader_text.append(QString("\n"
|
||||
"uniform sampler3D tex2;\n"
|
||||
"\n"
|
||||
"vec4 %2(vec4 col) {\n"
|
||||
" return %1\n"
|
||||
"}\n").arg(shader_call, process_function_name));
|
||||
shader_text.append(QStringLiteral("\n"
|
||||
"uniform sampler3D ove_ociolut;\n"
|
||||
"\n"
|
||||
"vec4 %2(vec4 col) {\n"
|
||||
" return %1\n"
|
||||
"}\n").arg(shader_call, process_function_name));
|
||||
|
||||
|
||||
// Get pipeline-based shader to inject OCIO shader into
|
||||
@@ -139,10 +138,10 @@ QString OpenGLShader::CodeDefaultFragment(const QString &function_name, const QS
|
||||
"precision highp float;\n"
|
||||
"#endif\n"
|
||||
"\n"
|
||||
"uniform sampler2D texture;\n"
|
||||
"uniform sampler2D ove_maintex;\n"
|
||||
"uniform bool color_only;\n"
|
||||
"uniform vec4 color_only_color;\n"
|
||||
"varying vec2 v_texcoord;\n"
|
||||
"varying vec2 ove_texcoord;\n"
|
||||
"\n");
|
||||
|
||||
// Finish the function with the main function
|
||||
@@ -157,7 +156,7 @@ QString OpenGLShader::CodeDefaultFragment(const QString &function_name, const QS
|
||||
" if (color_only) {\n"
|
||||
" gl_FragColor = color_only_color;"
|
||||
" } else {\n"
|
||||
" vec4 color = texture2D(texture, v_texcoord);\n"
|
||||
" vec4 color = texture2D(ove_maintex, ove_texcoord);\n"
|
||||
" gl_FragColor = color;\n"
|
||||
" }\n"
|
||||
"}\n"));
|
||||
@@ -167,13 +166,13 @@ QString OpenGLShader::CodeDefaultFragment(const QString &function_name, const QS
|
||||
// If additional code was passed, add it and reference it in main().
|
||||
//
|
||||
// The function in the additional code is expected to be `vec4 function_name(vec4 color)`. The texture coordinate
|
||||
// can be acquired through `v_texcoord`.
|
||||
// can be acquired through `ove_texcoord`.
|
||||
|
||||
frag_code.append(shader_code);
|
||||
|
||||
frag_code.append(QStringLiteral("\n"
|
||||
"void main() {\n"
|
||||
" vec4 color = %1(texture2D(texture, v_texcoord));\n"
|
||||
" vec4 color = %1(texture2D(ove_maintex, ove_texcoord));\n"
|
||||
" gl_FragColor = color;\n"
|
||||
"}\n").arg(function_name));
|
||||
|
||||
@@ -192,16 +191,16 @@ QString OpenGLShader::CodeDefaultVertex()
|
||||
"precision highp float;\n"
|
||||
"#endif\n"
|
||||
"\n"
|
||||
"uniform mat4 mvp_matrix;\n"
|
||||
"uniform mat4 ove_mvpmat;\n"
|
||||
"\n"
|
||||
"attribute vec4 a_position;\n"
|
||||
"attribute vec2 a_texcoord;\n"
|
||||
"\n"
|
||||
"varying vec2 v_texcoord;\n"
|
||||
"varying vec2 ove_texcoord;\n"
|
||||
"\n"
|
||||
"void main() {\n"
|
||||
" gl_Position = mvp_matrix * a_position;\n"
|
||||
" v_texcoord = a_texcoord;\n"
|
||||
" gl_Position = ove_mvpmat * a_position;\n"
|
||||
" ove_texcoord = a_texcoord;\n"
|
||||
"}\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ void OpenGLWorker::ParametersChangedEvent()
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLWorker::RunNodeAccelerated(Node *node, const NodeValueDatabase *input_params, NodeValueTable *output_params)
|
||||
void OpenGLWorker::RunNodeAccelerated(const Node *node, const NodeValueDatabase *input_params, NodeValueTable *output_params)
|
||||
{
|
||||
OpenGLShaderPtr shader = shader_cache_->Get(node->id());
|
||||
|
||||
@@ -174,15 +174,7 @@ void OpenGLWorker::RunNodeAccelerated(Node *node, const NodeValueDatabase *input
|
||||
// Get value from database at this input
|
||||
const NodeValueTable& input_data = (*input_params)[input];
|
||||
|
||||
NodeParam::DataType find_data_type = input->data_type();
|
||||
|
||||
// Exception for Footage types (try to get a Texture instead)
|
||||
if (find_data_type == NodeParam::kFootage) {
|
||||
find_data_type = NodeParam::kTexture;
|
||||
}
|
||||
|
||||
// Try to get a value from it
|
||||
QVariant value = input_data.Get(find_data_type);
|
||||
QVariant value = node->InputValueFromTable(input, input_data);
|
||||
|
||||
switch (input->data_type()) {
|
||||
case NodeInput::kInt:
|
||||
@@ -225,6 +217,16 @@ void OpenGLWorker::RunNodeAccelerated(Node *node, const NodeValueDatabase *input
|
||||
// Set value to bound texture
|
||||
shader->setUniformValue(variable_location, input_texture_count);
|
||||
|
||||
// Set texture resolution if shader wants it
|
||||
int res_param_location = shader->uniformLocation(QStringLiteral("%1_resolution").arg(input->id()));
|
||||
if (res_param_location > -1) {
|
||||
shader->setUniformValue(res_param_location,
|
||||
static_cast<GLfloat>(texture->width()),
|
||||
static_cast<GLfloat>(texture->height()));
|
||||
}
|
||||
|
||||
olive::gl::PrepareToDraw(functions_);
|
||||
|
||||
input_texture_count++;
|
||||
break;
|
||||
}
|
||||
@@ -247,9 +249,14 @@ void OpenGLWorker::RunNodeAccelerated(Node *node, const NodeValueDatabase *input
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure viewport is correct
|
||||
// Set up OpenGL parameters as necessary
|
||||
functions_->glViewport(0, 0, video_params().effective_width(), video_params().effective_height());
|
||||
|
||||
// Provide some standard args
|
||||
shader->setUniformValue("ove_resolution",
|
||||
static_cast<GLfloat>(video_params().width()),
|
||||
static_cast<GLfloat>(video_params().height()));
|
||||
|
||||
// Blit this texture through this shader
|
||||
olive::gl::Blit(shader);
|
||||
|
||||
@@ -305,9 +312,8 @@ void OpenGLWorker::FinishInit()
|
||||
|
||||
// Store OpenGL functions instance
|
||||
functions_ = ctx_->functions();
|
||||
functions_->glBlendFunc(GL_ONE, GL_ZERO);
|
||||
|
||||
// Set up OpenGL parameters as necessary
|
||||
functions_->glEnable(GL_BLEND);
|
||||
ParametersChangedEvent();
|
||||
|
||||
buffer_.Create(ctx_);
|
||||
|
||||
@@ -46,7 +46,7 @@ protected:
|
||||
|
||||
virtual void FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable* table) override;
|
||||
|
||||
virtual void RunNodeAccelerated(Node *node, const NodeValueDatabase *input_params, NodeValueTable* output_params) override;
|
||||
virtual void RunNodeAccelerated(const Node *node, const NodeValueDatabase *input_params, NodeValueTable* output_params) override;
|
||||
|
||||
virtual void TextureToBuffer(const QVariant& texture, QByteArray& buffer) override;
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@ RenderBackend::RenderBackend(QObject *parent) :
|
||||
started_(false),
|
||||
viewer_node_(nullptr),
|
||||
copied_viewer_node_(nullptr),
|
||||
value_update_queued_(false),
|
||||
recompile_queued_(false),
|
||||
input_update_queued_(false)
|
||||
input_update_queued_(false),
|
||||
blocking_(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@ void RenderBackend::Close()
|
||||
|
||||
started_ = false;
|
||||
|
||||
Decompile();
|
||||
|
||||
CloseInternal();
|
||||
|
||||
for (int i=0;i<processors_.size();i++) {
|
||||
@@ -127,12 +129,14 @@ bool RenderBackend::Compile()
|
||||
foreach (Node* n, source_node_list_) {
|
||||
Node* copy = n->copy();
|
||||
|
||||
// Copy values (but not connections yet)
|
||||
Node::CopyInputs(n, copy, false);
|
||||
|
||||
copied_graph_.AddNode(copy);
|
||||
}
|
||||
|
||||
// We just copied the inputs, so if an input update is queued, it's unnecessary
|
||||
input_update_queued_ = false;
|
||||
|
||||
// We know that the first node will be the viewer node since we appended that first in the copy
|
||||
copied_viewer_node_ = static_cast<ViewerOutput*>(copied_graph_.nodes().first());
|
||||
|
||||
@@ -183,7 +187,7 @@ void RenderBackend::RegenerateCacheID()
|
||||
CacheIDChangedEvent(cache_id_);
|
||||
}
|
||||
|
||||
rational RenderBackend::SequenceLength()
|
||||
rational RenderBackend::GetSequenceLength()
|
||||
{
|
||||
if (viewer_node_ == nullptr) {
|
||||
return 0;
|
||||
@@ -216,16 +220,24 @@ void RenderBackend::CacheNext()
|
||||
}
|
||||
|
||||
if ((input_update_queued_ || recompile_queued_) && !AllProcessorsAreAvailable()) {
|
||||
qDebug() << "Blocking render while we wait for all workers to finish...";
|
||||
if (input_update_queued_ && recompile_queued_) {
|
||||
qDebug() << "Input update and recompile queued, blocking while processors are busy...";
|
||||
} else if (input_update_queued_) {
|
||||
qDebug() << "Input update queued, blocking while processors are busy...";
|
||||
} else {
|
||||
qDebug() << "Recompile queued, blocking while processors are busy...";
|
||||
}
|
||||
blocking_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (input_update_queued_) {
|
||||
UpdateNodeInputs();
|
||||
input_update_queued_ = false;
|
||||
if (blocking_) {
|
||||
qDebug() << "Processors are free now";
|
||||
blocking_ = false;
|
||||
}
|
||||
|
||||
if (recompile_queued_) {
|
||||
qDebug() << "Recompile requested, decompiling now...";
|
||||
Decompile();
|
||||
recompile_queued_ = false;
|
||||
}
|
||||
@@ -235,6 +247,25 @@ void RenderBackend::CacheNext()
|
||||
return;
|
||||
}
|
||||
|
||||
if (input_update_queued_) {
|
||||
qDebug() << "Copied inputs from UI graph";
|
||||
|
||||
for (int i=0;i<source_node_list_.size();i++) {
|
||||
Node* src = source_node_list_.at(i);
|
||||
Node* dst = copied_graph_.nodes().at(i);
|
||||
|
||||
Node::CopyInputs(src, dst, false);
|
||||
}
|
||||
|
||||
input_update_queued_ = false;
|
||||
}
|
||||
|
||||
Node* node_connected_to_viewer = GetDependentInput()->get_connected_node();
|
||||
|
||||
if (!node_connected_to_viewer) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (RenderWorker* worker, processors_) {
|
||||
if (cache_queue_.isEmpty()) {
|
||||
break;
|
||||
@@ -243,14 +274,16 @@ void RenderBackend::CacheNext()
|
||||
if (!WorkerIsBusy(worker)) {
|
||||
TimeRange cache_frame = cache_queue_.takeFirst();
|
||||
|
||||
NodeDependency dep = NodeDependency(GetDependentInput()->get_connected_node(), cache_frame.in(), cache_frame.out());
|
||||
//qDebug() << "Rendering" << cache_frame.in().toDouble() << "-" << cache_frame.out().toDouble();
|
||||
|
||||
NodeDependency dep = NodeDependency(node_connected_to_viewer, cache_frame.in(), cache_frame.out());
|
||||
|
||||
SetWorkerBusyState(worker, true);
|
||||
|
||||
QMetaObject::invokeMethod(worker,
|
||||
"Render",
|
||||
Qt::QueuedConnection,
|
||||
Q_ARG(NodeDependency, dep));
|
||||
|
||||
SetWorkerBusyState(worker, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -270,24 +303,9 @@ const QString &RenderBackend::cache_id() const
|
||||
return cache_id_;
|
||||
}
|
||||
|
||||
void RenderBackend::QueueValueUpdate(const TimeRange &range)
|
||||
void RenderBackend::QueueValueUpdate()
|
||||
{
|
||||
value_update_queued_ = true;
|
||||
value_update_range_ = range;
|
||||
}
|
||||
|
||||
void RenderBackend::UpdateNodeInputs()
|
||||
{
|
||||
if (value_update_queued_) {
|
||||
for (int i=0;i<source_node_list_.size();i++) {
|
||||
Node* src = source_node_list_.at(i);
|
||||
Node* dst = copied_graph_.nodes().at(i);
|
||||
|
||||
Node::CopyInputs(src, dst, false);
|
||||
}
|
||||
|
||||
value_update_queued_ = false;
|
||||
}
|
||||
input_update_queued_ = true;
|
||||
}
|
||||
|
||||
bool RenderBackend::WorkerIsBusy(RenderWorker *worker) const
|
||||
|
||||
@@ -47,7 +47,7 @@ protected:
|
||||
|
||||
virtual void DecompileInternal() = 0;
|
||||
|
||||
rational SequenceLength();
|
||||
rational GetSequenceLength();
|
||||
|
||||
const QVector<QThread*>& threads();
|
||||
|
||||
@@ -82,9 +82,7 @@ protected:
|
||||
|
||||
const QString& cache_id() const;
|
||||
|
||||
void QueueValueUpdate(const TimeRange& range);
|
||||
|
||||
void UpdateNodeInputs();
|
||||
void QueueValueUpdate();
|
||||
|
||||
bool WorkerIsBusy(RenderWorker* worker) const;
|
||||
void SetWorkerBusyState(RenderWorker* worker, bool busy);
|
||||
@@ -130,14 +128,13 @@ private:
|
||||
QList<Node*> source_node_list_;
|
||||
NodeGraph copied_graph_;
|
||||
|
||||
bool value_update_queued_;
|
||||
TimeRange value_update_range_;
|
||||
|
||||
bool recompile_queued_;
|
||||
bool input_update_queued_;
|
||||
|
||||
QVector<bool> processor_busy_state_;
|
||||
|
||||
bool blocking_;
|
||||
|
||||
private slots:
|
||||
void QueueRecompile();
|
||||
|
||||
|
||||
@@ -37,31 +37,9 @@ void RenderWorker::Render(NodeDependency path)
|
||||
emit CompletedCache(path, RenderInternal(path));
|
||||
}
|
||||
|
||||
NodeValueTable RenderWorker::RenderAsSibling(NodeDependency dep)
|
||||
{
|
||||
Node* node = dep.node();
|
||||
QList<NodeInput*> connected_inputs;
|
||||
NodeValueTable value;
|
||||
|
||||
//qDebug() << "Processing" << node->id();
|
||||
|
||||
// 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->IsTrack()) {
|
||||
// If the range is not wholly contained in this Block, we'll need to do some extra processing
|
||||
value = RenderBlock(static_cast<TrackOutput*>(node), dep.range());
|
||||
} else {
|
||||
value = ProcessNodeNormally(NodeDependency(node, dep.range()));
|
||||
}
|
||||
|
||||
// We're done!
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
NodeValueTable RenderWorker::RenderInternal(const NodeDependency &path)
|
||||
{
|
||||
return RenderAsSibling(path);
|
||||
return ProcessNode(path);
|
||||
}
|
||||
|
||||
bool RenderWorker::OutputIsAccelerated(Node *output)
|
||||
@@ -70,7 +48,7 @@ bool RenderWorker::OutputIsAccelerated(Node *output)
|
||||
return false;
|
||||
}
|
||||
|
||||
void RenderWorker::RunNodeAccelerated(Node *node, const NodeValueDatabase *input_params, NodeValueTable* output_params)
|
||||
void RenderWorker::RunNodeAccelerated(const Node *node, const NodeValueDatabase *input_params, NodeValueTable* output_params)
|
||||
{
|
||||
Q_UNUSED(node)
|
||||
Q_UNUSED(input_params)
|
||||
@@ -102,9 +80,14 @@ bool RenderWorker::IsStarted()
|
||||
return started_;
|
||||
}
|
||||
|
||||
NodeValueTable RenderWorker::ProcessNodeNormally(const NodeDependency& dep)
|
||||
NodeValueTable RenderWorker::ProcessNode(const NodeDependency& dep)
|
||||
{
|
||||
Node* node = dep.node();
|
||||
const Node* node = dep.node();
|
||||
|
||||
if (node->IsTrack()) {
|
||||
// If the range is not wholly contained in this Block, we'll need to do some extra processing
|
||||
return RenderBlock(static_cast<const TrackOutput*>(node), dep.range());
|
||||
}
|
||||
|
||||
// FIXME: Cache certain values here if we've already processed them before
|
||||
|
||||
@@ -119,8 +102,8 @@ NodeValueTable RenderWorker::ProcessNodeNormally(const NodeDependency& dep)
|
||||
|
||||
if (input->IsConnected()) {
|
||||
// Value will equal something from the connected node, follow it
|
||||
table = ProcessNodeNormally(NodeDependency(input->get_connected_node(),
|
||||
input_time));
|
||||
table = ProcessNode(NodeDependency(input->get_connected_node(),
|
||||
input_time));
|
||||
} else {
|
||||
// Push onto the table the value at this time from the input
|
||||
QVariant input_value = input->get_value_at_time(input_time.in());
|
||||
|
||||
@@ -25,8 +25,6 @@ public slots:
|
||||
|
||||
void Render(NodeDependency path);
|
||||
|
||||
NodeValueTable RenderAsSibling(NodeDependency dep);
|
||||
|
||||
signals:
|
||||
void CompletedCache(NodeDependency dep, NodeValueTable data);
|
||||
|
||||
@@ -39,7 +37,7 @@ protected:
|
||||
|
||||
virtual bool OutputIsAccelerated(Node *output);
|
||||
|
||||
virtual void RunNodeAccelerated(Node *node, const NodeValueDatabase *input_params, NodeValueTable* output_params);
|
||||
virtual void RunNodeAccelerated(const Node *node, const NodeValueDatabase *input_params, NodeValueTable* output_params);
|
||||
|
||||
StreamPtr ResolveStreamFromInput(NodeInput* input);
|
||||
DecoderPtr ResolveDecoderFromInput(StreamPtr stream);
|
||||
@@ -48,9 +46,9 @@ protected:
|
||||
|
||||
virtual void FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable* table) = 0;
|
||||
|
||||
NodeValueTable ProcessNodeNormally(const NodeDependency &dep);
|
||||
NodeValueTable ProcessNode(const NodeDependency &dep);
|
||||
|
||||
virtual NodeValueTable RenderBlock(TrackOutput *track, const TimeRange& range) = 0;
|
||||
virtual NodeValueTable RenderBlock(const TrackOutput *track, const TimeRange& range) = 0;
|
||||
|
||||
private:
|
||||
bool started_;
|
||||
|
||||
@@ -47,7 +47,7 @@ void VideoRenderBackend::InvalidateCache(const rational &start_range, const rati
|
||||
|
||||
// Adjust range to min/max values
|
||||
rational start_range_adj = qMax(rational(0), start_range);
|
||||
rational end_range_adj = qMin(SequenceLength(), end_range);
|
||||
rational end_range_adj = qMin(GetSequenceLength(), end_range);
|
||||
|
||||
qDebug() << "Cache invalidated between"
|
||||
<< start_range_adj.toDouble()
|
||||
@@ -99,10 +99,10 @@ void VideoRenderBackend::InvalidateCache(const rational &start_range, const rati
|
||||
}
|
||||
|
||||
// Remove frames after this time code if it's changed
|
||||
frame_cache_.Truncate(SequenceLength());
|
||||
frame_cache_.Truncate(GetSequenceLength());
|
||||
|
||||
// Queue value update
|
||||
QueueValueUpdate(TimeRange(start_range, end_range));
|
||||
QueueValueUpdate();
|
||||
|
||||
CacheNext();
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ NodeValueTable VideoRenderWorker::RenderInternal(const NodeDependency& path)
|
||||
emit HashAlreadyExists(path, hash);
|
||||
} else if (frame_cache_->TryCache(hash)) {
|
||||
// This hash is available for us to cache, start traversing graph
|
||||
value = RenderAsSibling(path);
|
||||
value = ProcessNode(path);
|
||||
|
||||
emit CompletedFrame(path, hash, value);
|
||||
} else {
|
||||
@@ -47,11 +47,11 @@ FramePtr VideoRenderWorker::RetrieveFromDecoder(DecoderPtr decoder, const TimeRa
|
||||
return decoder->RetrieveVideo(range.in());
|
||||
}
|
||||
|
||||
void VideoRenderWorker::HashNodeRecursively(QCryptographicHash *hash, Node* n, const rational& time)
|
||||
void VideoRenderWorker::HashNodeRecursively(QCryptographicHash *hash, const Node* n, const rational& time)
|
||||
{
|
||||
// Resolve BlockList
|
||||
if (n->IsTrack()) {
|
||||
n = static_cast<TrackOutput*>(n)->BlockAtTime(time);
|
||||
n = static_cast<const TrackOutput*>(n)->BlockAtTime(time);
|
||||
|
||||
if (!n) {
|
||||
return;
|
||||
@@ -66,52 +66,50 @@ void VideoRenderWorker::HashNodeRecursively(QCryptographicHash *hash, Node* n, c
|
||||
if (param->type() == NodeParam::kInput) {
|
||||
NodeInput* input = static_cast<NodeInput*>(param);
|
||||
|
||||
if (input->dependent()) {
|
||||
// Get time adjustment
|
||||
TimeRange range = n->InputTimeAdjustment(input, TimeRange(time, time));
|
||||
// Get time adjustment
|
||||
TimeRange range = n->InputTimeAdjustment(input, TimeRange(time, time));
|
||||
|
||||
// For a single frame, we only care about one of the times
|
||||
rational input_time = range.in();
|
||||
// For a single frame, we only care about one of the times
|
||||
rational input_time = range.in();
|
||||
|
||||
if (input->IsConnected()) {
|
||||
// Traverse down this edge
|
||||
HashNodeRecursively(hash, input->get_connected_node(), input_time);
|
||||
} else {
|
||||
// Grab the value at this time
|
||||
QVariant value = input->get_value_at_time(input_time);
|
||||
hash->addData(NodeParam::ValueToBytes(input->data_type(), value));
|
||||
}
|
||||
if (input->IsConnected()) {
|
||||
// Traverse down this edge
|
||||
HashNodeRecursively(hash, input->get_connected_node(), input_time);
|
||||
} else {
|
||||
// Grab the value at this time
|
||||
QVariant value = input->get_value_at_time(input_time);
|
||||
hash->addData(NodeParam::ValueToBytes(input->data_type(), value));
|
||||
}
|
||||
|
||||
// We have one exception for FOOTAGE types, since we resolve the footage into a frame in the renderer
|
||||
if (input->data_type() == NodeParam::kFootage) {
|
||||
StreamPtr stream = ResolveStreamFromInput(input);
|
||||
DecoderPtr decoder = ResolveDecoderFromInput(stream);
|
||||
// We have one exception for FOOTAGE types, since we resolve the footage into a frame in the renderer
|
||||
if (input->data_type() == NodeParam::kFootage) {
|
||||
StreamPtr stream = ResolveStreamFromInput(input);
|
||||
DecoderPtr decoder = ResolveDecoderFromInput(stream);
|
||||
|
||||
if (decoder != nullptr) {
|
||||
// Add footage details to hash
|
||||
if (decoder != nullptr) {
|
||||
// Add footage details to hash
|
||||
|
||||
// Footage filename
|
||||
hash->addData(stream->footage()->filename().toUtf8());
|
||||
// Footage filename
|
||||
hash->addData(stream->footage()->filename().toUtf8());
|
||||
|
||||
// Footage last modified date
|
||||
hash->addData(stream->footage()->timestamp().toString().toUtf8());
|
||||
// Footage last modified date
|
||||
hash->addData(stream->footage()->timestamp().toString().toUtf8());
|
||||
|
||||
// Footage stream
|
||||
hash->addData(QString::number(stream->index()).toUtf8());
|
||||
// Footage stream
|
||||
hash->addData(QString::number(stream->index()).toUtf8());
|
||||
|
||||
if (stream->type() == Stream::kImage || stream->type() == Stream::kVideo) {
|
||||
ImageStreamPtr video_stream = std::static_pointer_cast<ImageStream>(stream);
|
||||
if (stream->type() == Stream::kImage || stream->type() == Stream::kVideo) {
|
||||
ImageStreamPtr video_stream = std::static_pointer_cast<ImageStream>(stream);
|
||||
|
||||
// Footage timestamp
|
||||
hash->addData(QString::number(decoder->GetTimestampFromTime(time)).toUtf8());
|
||||
// Footage timestamp
|
||||
hash->addData(QString::number(decoder->GetTimestampFromTime(time)).toUtf8());
|
||||
|
||||
// Current colorspace
|
||||
// FIXME: Handle empty colorspace...
|
||||
hash->addData(video_stream->colorspace().toUtf8());
|
||||
// Current colorspace
|
||||
// FIXME: Handle empty colorspace...
|
||||
hash->addData(video_stream->colorspace().toUtf8());
|
||||
|
||||
// Alpha associated setting
|
||||
hash->addData(QString::number(video_stream->premultiplied_alpha()).toUtf8());
|
||||
}
|
||||
// Alpha associated setting
|
||||
hash->addData(QString::number(video_stream->premultiplied_alpha()).toUtf8());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,7 +160,7 @@ void VideoRenderWorker::Download(NodeDependency dep, QByteArray hash, QVariant t
|
||||
}
|
||||
}
|
||||
|
||||
NodeValueTable VideoRenderWorker::RenderBlock(TrackOutput *track, const TimeRange &range)
|
||||
NodeValueTable VideoRenderWorker::RenderBlock(const TrackOutput *track, const TimeRange &range)
|
||||
{
|
||||
// A frame can only have one active block so we just validate the in point of the range
|
||||
Block* active_block = track->BlockAtTime(range.in());
|
||||
@@ -170,8 +168,8 @@ NodeValueTable VideoRenderWorker::RenderBlock(TrackOutput *track, const TimeRang
|
||||
NodeValueTable table;
|
||||
|
||||
if (active_block) {
|
||||
table = RenderAsSibling(NodeDependency(active_block,
|
||||
range));
|
||||
table = ProcessNode(NodeDependency(active_block,
|
||||
range));
|
||||
}
|
||||
|
||||
return table;
|
||||
|
||||
@@ -43,14 +43,12 @@ protected:
|
||||
|
||||
virtual FramePtr RetrieveFromDecoder(DecoderPtr decoder, const TimeRange& range) override;
|
||||
|
||||
virtual NodeValueTable RenderBlock(TrackOutput *track, const TimeRange& range) override;
|
||||
virtual NodeValueTable RenderBlock(const TrackOutput *track, const TimeRange& range) override;
|
||||
|
||||
ColorProcessorCache* color_cache();
|
||||
|
||||
private:
|
||||
void ProcessNode();
|
||||
|
||||
void HashNodeRecursively(QCryptographicHash* hash, Node *n, const rational &time);
|
||||
void HashNodeRecursively(QCryptographicHash* hash, const Node *n, const rational &time);
|
||||
|
||||
VideoRenderingParams video_params_;
|
||||
|
||||
|
||||
@@ -49,8 +49,6 @@ void NodeView::SetGraph(NodeGraph *graph)
|
||||
disconnect(graph_, SIGNAL(NodeRemoved(Node*)), this, SLOT(RemoveNode(Node*)));
|
||||
disconnect(graph_, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SLOT(AddEdge(NodeEdgePtr)));
|
||||
disconnect(graph_, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(RemoveEdge(NodeEdgePtr)));
|
||||
|
||||
graph_->Release();
|
||||
}
|
||||
|
||||
// Clear the scene of all UI objects
|
||||
|
||||
Reference in New Issue
Block a user