From 445d276cd01f860ddee45d6ba93fe5260aadfe64 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 23 Apr 2021 11:00:18 +1000 Subject: [PATCH] nodes: further optimized merge node to passthrough RGB blend textures --- .../crossdissolve/crossdissolvetransition.cpp | 2 +- app/node/distort/crop/cropdistortnode.cpp | 2 +- .../transform/transformdistortnode.cpp | 48 +++++++------ app/node/filter/blur/blur.cpp | 2 +- app/node/generator/polygon/polygon.cpp | 2 +- app/node/generator/text/text.cpp | 2 +- app/node/math/math/mathbase.cpp | 2 +- app/node/math/merge/merge.cpp | 52 +++++++------- app/node/traverser.cpp | 39 +++++++++- app/node/traverser.h | 2 + app/render/job/generatejob.h | 14 ++-- app/render/renderprocessor.cpp | 24 +------ app/render/texture.cpp | 8 ++- app/render/texture.h | 18 +++++ tests/CMakeLists.txt | 2 + tests/compositing/CMakeLists.txt | 17 +++++ tests/compositing/compositing-tests.cpp | 71 +++++++++++++++++++ tests/timeline/timeline-tests.cpp | 2 +- 18 files changed, 227 insertions(+), 82 deletions(-) create mode 100644 tests/compositing/CMakeLists.txt create mode 100644 tests/compositing/compositing-tests.cpp diff --git a/app/node/block/transition/crossdissolve/crossdissolvetransition.cpp b/app/node/block/transition/crossdissolve/crossdissolvetransition.cpp index 53a5fd2e5..07441e06d 100644 --- a/app/node/block/transition/crossdissolve/crossdissolvetransition.cpp +++ b/app/node/block/transition/crossdissolve/crossdissolvetransition.cpp @@ -63,7 +63,7 @@ void CrossDissolveTransition::ShaderJobEvent(NodeValueDatabase &value, ShaderJob { Q_UNUSED(value) - job.SetAlphaChannelRequired(true); + job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn); } void CrossDissolveTransition::SampleJobEvent(SampleBufferPtr from_samples, SampleBufferPtr to_samples, SampleBufferPtr out_samples, double time_in) const diff --git a/app/node/distort/crop/cropdistortnode.cpp b/app/node/distort/crop/cropdistortnode.cpp index 12066f422..7b1838206 100644 --- a/app/node/distort/crop/cropdistortnode.cpp +++ b/app/node/distort/crop/cropdistortnode.cpp @@ -68,7 +68,7 @@ NodeValueTable CropDistortNode::Value(const QString &output, NodeValueDatabase & job.InsertValue(this, kFeatherInput, value); job.InsertValue(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")), this)); - job.SetAlphaChannelRequired(true); + job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn); NodeValueTable table = value.Merge(); diff --git a/app/node/distort/transform/transformdistortnode.cpp b/app/node/distort/transform/transformdistortnode.cpp index 02d7514c7..b16f6ffed 100644 --- a/app/node/distort/transform/transformdistortnode.cpp +++ b/app/node/distort/transform/transformdistortnode.cpp @@ -62,10 +62,11 @@ NodeValueTable TransformDistortNode::Value(const QString &output, NodeValueDatab QMatrix4x4 generated_matrix = GenerateMatrix(value, true, false, false, false); // Pop texture - TexturePtr texture = value[kTextureInput].Take(NodeValue::kTexture).value(); + NodeValue texture_meta = value[kTextureInput].TakeWithMeta(NodeValue::kTexture); + TexturePtr texture = texture_meta.data().value(); // Merge table - NodeValueTable table = value.Merge(); + NodeValueTable table = value[kTextureInput]; // If we have a texture, generate a matrix and make it happen if (texture) { @@ -74,7 +75,7 @@ NodeValueTable TransformDistortNode::Value(const QString &output, NodeValueDatab if (real_matrix.isIdentity()) { // We don't expect any changes, just push as normal - table.Push(NodeValue::kTexture, QVariant::fromValue(texture), this); + table.Push(texture_meta); } else { // The matrix will transform things ShaderJob job; @@ -84,7 +85,7 @@ NodeValueTable TransformDistortNode::Value(const QString &output, NodeValueDatab // FIXME: This should be optimized, we can use matrix math to determine if this operation will // end up with gaps in the screen that will require an alpha channel. - job.SetAlphaChannelRequired(true); + job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn); table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); } @@ -103,6 +104,11 @@ ShaderCode TransformDistortNode::GetShaderCode(const QString &shader_id) const bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) { + TexturePtr tex = db[kTextureInput].Get(NodeValue::kTexture).value(); + if (!tex) { + return false; + } + // Store cursor position gizmo_drag_pos_ = p; @@ -137,7 +143,7 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) } // Store texture size - VideoParams texture_params = db[kTextureInput].Get(NodeValue::kTexture).value(); + VideoParams texture_params = tex->params(); QVector2D texture_sz(texture_params.square_pixel_width(), texture_params.height()); gizmo_scale_anchor_ = db[kAnchorInput].Get(NodeValue::kVec2).value() + texture_sz/2; @@ -310,9 +316,9 @@ void TransformDistortNode::GizmoRelease() void TransformDistortNode::Hash(const QString &output, QCryptographicHash &hash, const rational &time, const VideoParams &video_params) const { + // If not connected to output, this will produce nothing NodeOutput out = GetConnectedOutput(kTextureInput); if (!out.IsValid()) { - // No texture connected, this node will produce nothing return; } @@ -321,18 +327,17 @@ void TransformDistortNode::Hash(const QString &output, QCryptographicHash &hash, traverser.SetCacheVideoParams(video_params); NodeValueDatabase db = traverser.GenerateDatabase(this, output, TimeRange(time, time + video_params.frame_rate_as_time_base())); - VideoParams tex_params = db[kTextureInput].Get(NodeValue::kTexture).value(); - QMatrix4x4 matrix = GenerateMatrix(db, true, false, false, false); - matrix = GenerateAutoScaledMatrix(matrix, db, tex_params); + TexturePtr tex = db[kTextureInput].Get(NodeValue::kTexture).value(); + if (tex) { + VideoParams tex_params = tex->params(); + QMatrix4x4 matrix = GenerateMatrix(db, true, false, false, false); + matrix = GenerateAutoScaledMatrix(matrix, db, tex_params); - if (matrix.isIdentity()) { - qDebug() << "Detected identity matrix, skipping hashing"; - } else { - qDebug() << "Detected NON-IDENTITY, hashing..."; - - // Add fingerprint - hash.addData(id().toUtf8()); - hash.addData(reinterpret_cast(&matrix), sizeof(matrix)); + if (!matrix.isIdentity()) { + // Add fingerprint + hash.addData(id().toUtf8()); + hash.addData(reinterpret_cast(&matrix), sizeof(matrix)); + } } out.node()->Hash(out.output(), hash, time, video_params); @@ -394,8 +399,6 @@ QMatrix4x4 TransformDistortNode::GenerateAutoScaledMatrix(const QMatrix4x4& gene QVector2D texture_res(texture_params.square_pixel_width(), texture_params.height()); AutoScaleType autoscale = static_cast(value[kAutoscaleInput].Get(NodeValue::kCombo).toInt()); - qDebug() << "Doing transform with" << texture_params.square_pixel_width() << "x" << texture_params.height() << "vs" << sequence_res.x() << "x" << sequence_res.y(); - return AdjustMatrixByResolutions(generated_matrix, sequence_res, texture_res, @@ -404,6 +407,11 @@ QMatrix4x4 TransformDistortNode::GenerateAutoScaledMatrix(const QMatrix4x4& gene void TransformDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p) { + TexturePtr tex = db[kTextureInput].Get(NodeValue::kTexture).value(); + if (!tex) { + return; + } + // 0 pen width is always 1px wide despite any transform p->setPen(QPen(Qt::white, 0)); @@ -413,7 +421,7 @@ void TransformDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p) QPointF sequence_half_res_pt = sequence_half_res.toPointF(); // GizmoTraverser just returns the sizes of the textures and no other data - VideoParams tex_params = db[kTextureInput].Get(NodeValue::kTexture).value(); + VideoParams tex_params = tex->params(); QVector2D tex_sz(tex_params.square_pixel_width(), tex_params.height()); // Retrieve autoscale value diff --git a/app/node/filter/blur/blur.cpp b/app/node/filter/blur/blur.cpp index afdb3b07f..463e27fec 100644 --- a/app/node/filter/blur/blur.cpp +++ b/app/node/filter/blur/blur.cpp @@ -118,7 +118,7 @@ NodeValueTable BlurFilterNode::Value(const QString &output, NodeValueDatabase &v // If we're not repeating pixels, expect an alpha channel to appear if (!job.GetValue(kRepeatEdgePixelsInput).data().toBool()) { - job.SetAlphaChannelRequired(true); + job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn); } table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); diff --git a/app/node/generator/polygon/polygon.cpp b/app/node/generator/polygon/polygon.cpp index 0ccc88e78..f74bd5a97 100644 --- a/app/node/generator/polygon/polygon.cpp +++ b/app/node/generator/polygon/polygon.cpp @@ -95,7 +95,7 @@ NodeValueTable PolygonGenerator::Value(const QString &output, NodeValueDatabase job.InsertValue(this, kPointsInput, value); job.InsertValue(this, kColorInput, value); job.InsertValue(QStringLiteral("resolution_in"), value[QStringLiteral("global")].GetWithMeta(NodeValue::kVec2, QStringLiteral("resolution"))); - job.SetAlphaChannelRequired(true); + job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn); NodeValueTable table = value.Merge(); table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); diff --git a/app/node/generator/text/text.cpp b/app/node/generator/text/text.cpp index 443d75f83..aa6587d90 100644 --- a/app/node/generator/text/text.cpp +++ b/app/node/generator/text/text.cpp @@ -95,7 +95,7 @@ NodeValueTable TextGenerator::Value(const QString &output, NodeValueDatabase &va job.InsertValue(this, kVAlignInput, value); job.InsertValue(this, kFontInput, value); job.InsertValue(this, kFontSizeInput, value); - job.SetAlphaChannelRequired(true); + job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn); NodeValueTable table = value.Merge(); diff --git a/app/node/math/math/mathbase.cpp b/app/node/math/math/mathbase.cpp index 8c6562af9..8072de0dd 100644 --- a/app/node/math/math/mathbase.cpp +++ b/app/node/math/math/mathbase.cpp @@ -324,7 +324,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o NodeValue(NodeValue::kMatrix, adjusted_matrix, this)); // It's likely an alpha channel will result from this operation - job.SetAlphaChannelRequired(true); + job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn); } } diff --git a/app/node/math/merge/merge.cpp b/app/node/math/merge/merge.cpp index c2a56c700..137d710f8 100644 --- a/app/node/math/merge/merge.cpp +++ b/app/node/math/merge/merge.cpp @@ -20,6 +20,8 @@ #include "merge.h" +#include "node/traverser.h" + namespace olive { const QString MergeNode::kBaseIn = QStringLiteral("base_in"); @@ -93,6 +95,11 @@ NodeValueTable MergeNode::Value(const QString &output, NodeValueDatabase &value) table.Push(job.GetValue(kBaseIn)); } else { // We have both textures, push the job + if (base_tex->channel_count() < VideoParams::kRGBAChannelCount) { + // Base has no alpha, therefore this merge operation will not add an alpha channel + job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOff); + } + table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); } } @@ -102,37 +109,34 @@ NodeValueTable MergeNode::Value(const QString &output, NodeValueDatabase &value) void MergeNode::Hash(const QString &output, QCryptographicHash &hash, const rational &time, const VideoParams &video_params) const { - // We do some hash optimization here. If only one of the inputs is connected, this node - // functions as a passthrough so there's no alteration to the hash. The same is true if the - // connected node happens to return nothing (a gap for instance). Therefore we only add our - // fingerprint if the base AND the blend change the hash. Otherwise, we assume it's a passthrough. + NodeTraverser traverser; + traverser.SetCacheVideoParams(video_params); - Q_UNUSED(output) + NodeValueDatabase db = traverser.GenerateDatabase(this, output, TimeRange(time, time+video_params.frame_rate_as_time_base())); - QByteArray current_result = hash.result(); + TexturePtr base_tex = db[kBaseIn].Get(NodeValue::kTexture).value(); + TexturePtr blend_tex = db[kBlendIn].Get(NodeValue::kTexture).value(); - bool base_changed_hash = false; - bool blend_changed_hash = false; + if (base_tex || blend_tex) { + bool passthrough_base = !blend_tex; + bool passthrough_blend = !base_tex || (blend_tex && blend_tex->channel_count() < VideoParams::kRGBAChannelCount); - if (IsInputConnected(kBaseIn)) { - NodeOutput base_output = GetConnectedOutput(kBaseIn); - base_output.node()->Hash(base_output.output(), hash, time, video_params); + if (!passthrough_base && !passthrough_blend) { + // This merge will actually do something so we add a fingerprint + hash.addData(id().toUtf8()); + } - QByteArray post_base_hash = hash.result(); - base_changed_hash = (post_base_hash != current_result); - current_result = post_base_hash; - } + if (!passthrough_base) { + NodeOutput blend_output = GetConnectedOutput(kBlendIn); + blend_output.node()->Hash(blend_output.output(), hash, time, video_params); + } - if(IsInputConnected(kBlendIn)) { - NodeOutput blend_output = GetConnectedOutput(kBlendIn); - blend_output.node()->Hash(blend_output.output(), hash, time, video_params); + if (!passthrough_blend) { + NodeOutput base_output = GetConnectedOutput(kBaseIn); + base_output.node()->Hash(base_output.output(), hash, time, video_params); + } - blend_changed_hash = (hash.result() != current_result); - } - - if (base_changed_hash && blend_changed_hash) { - // Something changed, so we'll add our fingerprint - hash.addData(id().toUtf8()); + Q_ASSERT(!passthrough_base || !passthrough_blend); } } diff --git a/app/node/traverser.cpp b/app/node/traverser.cpp index 8b74399cd..29d603ece 100644 --- a/app/node/traverser.cpp +++ b/app/node/traverser.cpp @@ -45,6 +45,32 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const QStrin return database; } +int NodeTraverser::GetChannelCountFromJob(const GenerateJob &job) +{ + switch (job.GetAlphaChannelRequired()) { + case GenerateJob::kAlphaForceOn: + return VideoParams::kRGBAChannelCount; + case GenerateJob::kAlphaForceOff: + return VideoParams::kRGBChannelCount; + case GenerateJob::kAlphaAuto: + for (auto it=job.GetValues().cbegin(); it!=job.GetValues().cend(); it++) { + if (it.value().type() == NodeValue::kTexture) { + TexturePtr tex = it.value().data().value(); + if (tex && tex->channel_count() == VideoParams::kRGBAChannelCount) { + // An input texture has an alpha channel so assume we need one too + return VideoParams::kRGBAChannelCount; + } + } + } + + // No textures had alpha so assume we don't need one + return VideoParams::kRGBChannelCount; + } + + // Default fallback, should never get here + return VideoParams::kRGBAChannelCount; +} + NodeValueTable NodeTraverser::ProcessInput(const Node* node, const QString& input, const TimeRange& range) { // If input is connected, retrieve value directly @@ -134,7 +160,8 @@ QVariant NodeTraverser::ProcessVideoFootage(const FootageJob &stream, const rati { Q_UNUSED(input_time) - return QVariant::fromValue(stream.video_params()); + // Create dummy texture with footage params + return QVariant::fromValue(std::make_shared(stream.video_params())); } QVariant NodeTraverser::ProcessAudioFootage(const FootageJob& stream, const TimeRange &input_time) @@ -151,7 +178,10 @@ QVariant NodeTraverser::ProcessShader(const Node *node, const TimeRange &range, Q_UNUSED(range) Q_UNUSED(job) - return QVariant::fromValue(video_params_); + // Create dummy texture with sequence params + VideoParams tex_params = video_params_; + tex_params.set_channel_count(GetChannelCountFromJob(job)); + return QVariant::fromValue(std::make_shared(tex_params)); } QVariant NodeTraverser::ProcessSamples(const Node *node, const TimeRange &range, const SampleJob &job) @@ -168,7 +198,10 @@ QVariant NodeTraverser::ProcessFrameGeneration(const Node *node, const GenerateJ Q_UNUSED(node) Q_UNUSED(job) - return QVariant::fromValue(video_params_); + // Create dummy texture with sequence params + VideoParams tex_params = video_params_; + tex_params.set_channel_count(GetChannelCountFromJob(job)); + return QVariant::fromValue(std::make_shared(tex_params)); } void NodeTraverser::SaveCachedTexture(const QByteArray &hash, const QVariant &texture) diff --git a/app/node/traverser.h b/app/node/traverser.h index 3334e901f..b2b3698d7 100644 --- a/app/node/traverser.h +++ b/app/node/traverser.h @@ -54,6 +54,8 @@ public: video_params_ = params; } + static int GetChannelCountFromJob(const GenerateJob& job); + protected: NodeValueTable ProcessInput(const Node *node, const QString &input, const TimeRange &range); diff --git a/app/render/job/generatejob.h b/app/render/job/generatejob.h index 129e31cc6..f14149523 100644 --- a/app/render/job/generatejob.h +++ b/app/render/job/generatejob.h @@ -27,23 +27,29 @@ namespace olive { class GenerateJob : public AcceleratedJob { public: + enum AlphaChannelSetting { + kAlphaAuto, + kAlphaForceOn, + kAlphaForceOff + }; + GenerateJob() { - alpha_channel_required_ = false; + alpha_channel_required_ = kAlphaAuto; } - bool GetAlphaChannelRequired() const + AlphaChannelSetting GetAlphaChannelRequired() const { return alpha_channel_required_; } - void SetAlphaChannelRequired(bool e) + void SetAlphaChannelRequired(AlphaChannelSetting e) { alpha_channel_required_ = e; } private: - bool alpha_channel_required_; + AlphaChannelSetting alpha_channel_required_; }; diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 5cd1560f4..c1f403047 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -456,22 +456,7 @@ QVariant RenderProcessor::ProcessShader(const Node *node, const TimeRange &range VideoParams tex_params = ticket_->property("vparam").value(); - bool input_textures_have_alpha = false; - for (auto it=job.GetValues().cbegin(); it!=job.GetValues().cend(); it++) { - if (it.value().type() == NodeValue::kTexture) { - TexturePtr tex = it.value().data().value(); - if (tex && tex->channel_count() == VideoParams::kRGBAChannelCount) { - input_textures_have_alpha = true; - break; - } - } - } - - if (input_textures_have_alpha || job.GetAlphaChannelRequired()) { - tex_params.set_channel_count(VideoParams::kRGBAChannelCount); - } else { - tex_params.set_channel_count(VideoParams::kRGBChannelCount); - } + tex_params.set_channel_count(GetChannelCountFromJob(job)); TexturePtr destination = render_ctx_->CreateTexture(tex_params); @@ -521,12 +506,7 @@ QVariant RenderProcessor::ProcessFrameGeneration(const Node *node, const Generat FramePtr frame = Frame::Create(); VideoParams frame_params = ticket_->property("vparam").value(); - if (job.GetAlphaChannelRequired()) { - frame_params.set_channel_count(VideoParams::kRGBAChannelCount); - } else { - frame_params.set_channel_count(VideoParams::kRGBChannelCount); - } - + frame_params.set_channel_count(GetChannelCountFromJob(job)); frame->set_video_params(frame_params); frame->allocate(); diff --git a/app/render/texture.cpp b/app/render/texture.cpp index 464772d7d..cdd1be0cc 100644 --- a/app/render/texture.cpp +++ b/app/render/texture.cpp @@ -28,12 +28,16 @@ const Texture::Interpolation Texture::kDefaultInterpolation = Texture::kMipmappe Texture::~Texture() { - renderer_->DestroyNativeTexture(id_); + if (renderer_) { + renderer_->DestroyNativeTexture(id_); + } } void Texture::Upload(void *data, int linesize) { - renderer_->UploadToTexture(this, data, linesize); + if (renderer_) { + renderer_->UploadToTexture(this, data, linesize); + } } } diff --git a/app/render/texture.h b/app/render/texture.h index 2e168d986..10c8257c7 100644 --- a/app/render/texture.h +++ b/app/render/texture.h @@ -45,6 +45,19 @@ public: static const Interpolation kDefaultInterpolation; + /** + * @brief Construct a dummy texture with no renderer backend + */ + Texture(const VideoParams& param) : + renderer_(nullptr), + params_(param), + type_(k2D) + { + } + + /** + * @brief Construct a real texture linked to a renderer backend + */ Texture(Renderer* renderer, const QVariant& native, const VideoParams& param, Type type) : renderer_(renderer), params_(param), @@ -67,6 +80,11 @@ public: void Upload(void* data, int linesize); + bool IsDummy() const + { + return !renderer_; + } + int width() const { return params_.effective_width(); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 798d41583..d64bc20f5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -44,6 +44,7 @@ function(olive_add_test GROUP NAME SOURCE) ${NAME} PRIVATE ${CMAKE_SOURCE_DIR}/app + ${CMAKE_SOURCE_DIR}/tests ${OLIVE_INCLUDE_DIRS} ) target_link_libraries( @@ -64,4 +65,5 @@ function(olive_add_test GROUP NAME SOURCE) add_test(${NAME} ${NAME}) endfunction() +add_subdirectory(compositing) add_subdirectory(timeline) diff --git a/tests/compositing/CMakeLists.txt b/tests/compositing/CMakeLists.txt new file mode 100644 index 000000000..b0be21aaf --- /dev/null +++ b/tests/compositing/CMakeLists.txt @@ -0,0 +1,17 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2021 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +olive_add_test(Node compositing-tests compositing-tests.cpp) diff --git a/tests/compositing/compositing-tests.cpp b/tests/compositing/compositing-tests.cpp new file mode 100644 index 000000000..d5d773c37 --- /dev/null +++ b/tests/compositing/compositing-tests.cpp @@ -0,0 +1,71 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2021 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "testutil.h" + +#include "node/distort/transform/transformdistortnode.h" +#include "node/generator/solid/solid.h" +#include "node/math/merge/merge.h" +#include "node/project/project.h" +#include "render/rendermanager.h" + +namespace olive { + +OLIVE_ADD_TEST(MergeRGBOptimization) +{ + Project project; + + // Solids generate RGB buffers, which the merge node should optimize by passing through the blend + SolidGenerator* source_1 = new SolidGenerator(); + source_1->setParent(&project); + source_1->SetStandardValue(SolidGenerator::kColorInput, QVariant::fromValue(Color(1.0, 0.0, 0.0))); + + SolidGenerator* source_2 = new SolidGenerator(); + source_2->setParent(&project); + source_2->SetStandardValue(SolidGenerator::kColorInput, QVariant::fromValue(Color(0.0, 1.0, 0.0))); + + MergeNode* merge_1 = new MergeNode(); + merge_1->setParent(&project); + + MergeNode* merge_2 = new MergeNode(); + merge_2->setParent(&project); + + Node::ConnectEdge(source_2, NodeInput(merge_1, MergeNode::kBaseIn)); + Node::ConnectEdge(source_1, NodeInput(merge_1, MergeNode::kBlendIn)); + Node::ConnectEdge(source_1, NodeInput(merge_2, MergeNode::kBaseIn)); + Node::ConnectEdge(source_2, NodeInput(merge_2, MergeNode::kBlendIn)); + + VideoParams params(1920, 1080, rational(1, 30), VideoParams::kFormatFloat16, VideoParams::kRGBAChannelCount); + + QByteArray source_1_hash = RenderManager::Hash(source_1, SolidGenerator::kDefaultOutput, params, 0); + QByteArray source_2_hash = RenderManager::Hash(source_2, SolidGenerator::kDefaultOutput, params, 0); + QByteArray merge_1_hash = RenderManager::Hash(merge_1, MergeNode::kDefaultOutput, params, 0); + QByteArray merge_2_hash = RenderManager::Hash(merge_2, MergeNode::kDefaultOutput, params, 0); + + // Merge 1 should pass through to source 1 since source 1 is RGB and its blend + OLIVE_ASSERT(source_1_hash == merge_1_hash); + + // Merge 2 should pass through to source 2 since source 2 is RGB and its blend + OLIVE_ASSERT(source_2_hash == merge_2_hash); + + OLIVE_TEST_END; +} + +} diff --git a/tests/timeline/timeline-tests.cpp b/tests/timeline/timeline-tests.cpp index c0f0d4a88..ef85fdada 100644 --- a/tests/timeline/timeline-tests.cpp +++ b/tests/timeline/timeline-tests.cpp @@ -26,7 +26,7 @@ #include "node/project/sequence/sequence.h" #include "undo/undocommand.h" #include "widget/timelinewidget/timelineundo.h" -#include "../testutil.h" +#include "testutil.h" namespace olive {