nodes: further optimized merge node to passthrough RGB blend textures
This commit is contained in:
@@ -63,7 +63,7 @@ void CrossDissolveTransition::ShaderJobEvent(NodeValueDatabase &value, ShaderJob
|
|||||||
{
|
{
|
||||||
Q_UNUSED(value)
|
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
|
void CrossDissolveTransition::SampleJobEvent(SampleBufferPtr from_samples, SampleBufferPtr to_samples, SampleBufferPtr out_samples, double time_in) const
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ NodeValueTable CropDistortNode::Value(const QString &output, NodeValueDatabase &
|
|||||||
job.InsertValue(this, kFeatherInput, value);
|
job.InsertValue(this, kFeatherInput, value);
|
||||||
job.InsertValue(QStringLiteral("resolution_in"),
|
job.InsertValue(QStringLiteral("resolution_in"),
|
||||||
NodeValue(NodeValue::kVec2, value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")), this));
|
NodeValue(NodeValue::kVec2, value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")), this));
|
||||||
job.SetAlphaChannelRequired(true);
|
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||||
|
|
||||||
NodeValueTable table = value.Merge();
|
NodeValueTable table = value.Merge();
|
||||||
|
|
||||||
|
|||||||
@@ -62,10 +62,11 @@ NodeValueTable TransformDistortNode::Value(const QString &output, NodeValueDatab
|
|||||||
QMatrix4x4 generated_matrix = GenerateMatrix(value, true, false, false, false);
|
QMatrix4x4 generated_matrix = GenerateMatrix(value, true, false, false, false);
|
||||||
|
|
||||||
// Pop texture
|
// Pop texture
|
||||||
TexturePtr texture = value[kTextureInput].Take(NodeValue::kTexture).value<TexturePtr>();
|
NodeValue texture_meta = value[kTextureInput].TakeWithMeta(NodeValue::kTexture);
|
||||||
|
TexturePtr texture = texture_meta.data().value<TexturePtr>();
|
||||||
|
|
||||||
// Merge table
|
// Merge table
|
||||||
NodeValueTable table = value.Merge();
|
NodeValueTable table = value[kTextureInput];
|
||||||
|
|
||||||
// If we have a texture, generate a matrix and make it happen
|
// If we have a texture, generate a matrix and make it happen
|
||||||
if (texture) {
|
if (texture) {
|
||||||
@@ -74,7 +75,7 @@ NodeValueTable TransformDistortNode::Value(const QString &output, NodeValueDatab
|
|||||||
|
|
||||||
if (real_matrix.isIdentity()) {
|
if (real_matrix.isIdentity()) {
|
||||||
// We don't expect any changes, just push as normal
|
// We don't expect any changes, just push as normal
|
||||||
table.Push(NodeValue::kTexture, QVariant::fromValue(texture), this);
|
table.Push(texture_meta);
|
||||||
} else {
|
} else {
|
||||||
// The matrix will transform things
|
// The matrix will transform things
|
||||||
ShaderJob job;
|
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
|
// 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.
|
// 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);
|
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)
|
bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
|
||||||
{
|
{
|
||||||
|
TexturePtr tex = db[kTextureInput].Get(NodeValue::kTexture).value<TexturePtr>();
|
||||||
|
if (!tex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Store cursor position
|
// Store cursor position
|
||||||
gizmo_drag_pos_ = p;
|
gizmo_drag_pos_ = p;
|
||||||
|
|
||||||
@@ -137,7 +143,7 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Store texture size
|
// Store texture size
|
||||||
VideoParams texture_params = db[kTextureInput].Get(NodeValue::kTexture).value<VideoParams>();
|
VideoParams texture_params = tex->params();
|
||||||
QVector2D texture_sz(texture_params.square_pixel_width(), texture_params.height());
|
QVector2D texture_sz(texture_params.square_pixel_width(), texture_params.height());
|
||||||
gizmo_scale_anchor_ = db[kAnchorInput].Get(NodeValue::kVec2).value<QVector2D>() + texture_sz/2;
|
gizmo_scale_anchor_ = db[kAnchorInput].Get(NodeValue::kVec2).value<QVector2D>() + 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
|
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);
|
NodeOutput out = GetConnectedOutput(kTextureInput);
|
||||||
if (!out.IsValid()) {
|
if (!out.IsValid()) {
|
||||||
// No texture connected, this node will produce nothing
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,18 +327,17 @@ void TransformDistortNode::Hash(const QString &output, QCryptographicHash &hash,
|
|||||||
traverser.SetCacheVideoParams(video_params);
|
traverser.SetCacheVideoParams(video_params);
|
||||||
|
|
||||||
NodeValueDatabase db = traverser.GenerateDatabase(this, output, TimeRange(time, time + video_params.frame_rate_as_time_base()));
|
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<VideoParams>();
|
TexturePtr tex = db[kTextureInput].Get(NodeValue::kTexture).value<TexturePtr>();
|
||||||
QMatrix4x4 matrix = GenerateMatrix(db, true, false, false, false);
|
if (tex) {
|
||||||
matrix = GenerateAutoScaledMatrix(matrix, db, tex_params);
|
VideoParams tex_params = tex->params();
|
||||||
|
QMatrix4x4 matrix = GenerateMatrix(db, true, false, false, false);
|
||||||
|
matrix = GenerateAutoScaledMatrix(matrix, db, tex_params);
|
||||||
|
|
||||||
if (matrix.isIdentity()) {
|
if (!matrix.isIdentity()) {
|
||||||
qDebug() << "Detected identity matrix, skipping hashing";
|
// Add fingerprint
|
||||||
} else {
|
hash.addData(id().toUtf8());
|
||||||
qDebug() << "Detected NON-IDENTITY, hashing...";
|
hash.addData(reinterpret_cast<const char*>(&matrix), sizeof(matrix));
|
||||||
|
}
|
||||||
// Add fingerprint
|
|
||||||
hash.addData(id().toUtf8());
|
|
||||||
hash.addData(reinterpret_cast<const char*>(&matrix), sizeof(matrix));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out.node()->Hash(out.output(), hash, time, video_params);
|
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());
|
QVector2D texture_res(texture_params.square_pixel_width(), texture_params.height());
|
||||||
AutoScaleType autoscale = static_cast<AutoScaleType>(value[kAutoscaleInput].Get(NodeValue::kCombo).toInt());
|
AutoScaleType autoscale = static_cast<AutoScaleType>(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,
|
return AdjustMatrixByResolutions(generated_matrix,
|
||||||
sequence_res,
|
sequence_res,
|
||||||
texture_res,
|
texture_res,
|
||||||
@@ -404,6 +407,11 @@ QMatrix4x4 TransformDistortNode::GenerateAutoScaledMatrix(const QMatrix4x4& gene
|
|||||||
|
|
||||||
void TransformDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p)
|
void TransformDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p)
|
||||||
{
|
{
|
||||||
|
TexturePtr tex = db[kTextureInput].Get(NodeValue::kTexture).value<TexturePtr>();
|
||||||
|
if (!tex) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 0 pen width is always 1px wide despite any transform
|
// 0 pen width is always 1px wide despite any transform
|
||||||
p->setPen(QPen(Qt::white, 0));
|
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();
|
QPointF sequence_half_res_pt = sequence_half_res.toPointF();
|
||||||
|
|
||||||
// GizmoTraverser just returns the sizes of the textures and no other data
|
// GizmoTraverser just returns the sizes of the textures and no other data
|
||||||
VideoParams tex_params = db[kTextureInput].Get(NodeValue::kTexture).value<VideoParams>();
|
VideoParams tex_params = tex->params();
|
||||||
QVector2D tex_sz(tex_params.square_pixel_width(), tex_params.height());
|
QVector2D tex_sz(tex_params.square_pixel_width(), tex_params.height());
|
||||||
|
|
||||||
// Retrieve autoscale value
|
// Retrieve autoscale value
|
||||||
|
|||||||
@@ -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 we're not repeating pixels, expect an alpha channel to appear
|
||||||
if (!job.GetValue(kRepeatEdgePixelsInput).data().toBool()) {
|
if (!job.GetValue(kRepeatEdgePixelsInput).data().toBool()) {
|
||||||
job.SetAlphaChannelRequired(true);
|
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||||
}
|
}
|
||||||
|
|
||||||
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ NodeValueTable PolygonGenerator::Value(const QString &output, NodeValueDatabase
|
|||||||
job.InsertValue(this, kPointsInput, value);
|
job.InsertValue(this, kPointsInput, value);
|
||||||
job.InsertValue(this, kColorInput, value);
|
job.InsertValue(this, kColorInput, value);
|
||||||
job.InsertValue(QStringLiteral("resolution_in"), value[QStringLiteral("global")].GetWithMeta(NodeValue::kVec2, QStringLiteral("resolution")));
|
job.InsertValue(QStringLiteral("resolution_in"), value[QStringLiteral("global")].GetWithMeta(NodeValue::kVec2, QStringLiteral("resolution")));
|
||||||
job.SetAlphaChannelRequired(true);
|
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||||
|
|
||||||
NodeValueTable table = value.Merge();
|
NodeValueTable table = value.Merge();
|
||||||
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ NodeValueTable TextGenerator::Value(const QString &output, NodeValueDatabase &va
|
|||||||
job.InsertValue(this, kVAlignInput, value);
|
job.InsertValue(this, kVAlignInput, value);
|
||||||
job.InsertValue(this, kFontInput, value);
|
job.InsertValue(this, kFontInput, value);
|
||||||
job.InsertValue(this, kFontSizeInput, value);
|
job.InsertValue(this, kFontSizeInput, value);
|
||||||
job.SetAlphaChannelRequired(true);
|
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||||
|
|
||||||
NodeValueTable table = value.Merge();
|
NodeValueTable table = value.Merge();
|
||||||
|
|
||||||
|
|||||||
@@ -324,7 +324,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
|
|||||||
NodeValue(NodeValue::kMatrix, adjusted_matrix, this));
|
NodeValue(NodeValue::kMatrix, adjusted_matrix, this));
|
||||||
|
|
||||||
// It's likely an alpha channel will result from this operation
|
// It's likely an alpha channel will result from this operation
|
||||||
job.SetAlphaChannelRequired(true);
|
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
#include "merge.h"
|
#include "merge.h"
|
||||||
|
|
||||||
|
#include "node/traverser.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
const QString MergeNode::kBaseIn = QStringLiteral("base_in");
|
const QString MergeNode::kBaseIn = QStringLiteral("base_in");
|
||||||
@@ -93,6 +95,11 @@ NodeValueTable MergeNode::Value(const QString &output, NodeValueDatabase &value)
|
|||||||
table.Push(job.GetValue(kBaseIn));
|
table.Push(job.GetValue(kBaseIn));
|
||||||
} else {
|
} else {
|
||||||
// We have both textures, push the job
|
// 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);
|
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
|
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
|
NodeTraverser traverser;
|
||||||
// functions as a passthrough so there's no alteration to the hash. The same is true if the
|
traverser.SetCacheVideoParams(video_params);
|
||||||
// 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.
|
|
||||||
|
|
||||||
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>();
|
||||||
|
TexturePtr blend_tex = db[kBlendIn].Get(NodeValue::kTexture).value<TexturePtr>();
|
||||||
|
|
||||||
bool base_changed_hash = false;
|
if (base_tex || blend_tex) {
|
||||||
bool blend_changed_hash = false;
|
bool passthrough_base = !blend_tex;
|
||||||
|
bool passthrough_blend = !base_tex || (blend_tex && blend_tex->channel_count() < VideoParams::kRGBAChannelCount);
|
||||||
|
|
||||||
if (IsInputConnected(kBaseIn)) {
|
if (!passthrough_base && !passthrough_blend) {
|
||||||
NodeOutput base_output = GetConnectedOutput(kBaseIn);
|
// This merge will actually do something so we add a fingerprint
|
||||||
base_output.node()->Hash(base_output.output(), hash, time, video_params);
|
hash.addData(id().toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray post_base_hash = hash.result();
|
if (!passthrough_base) {
|
||||||
base_changed_hash = (post_base_hash != current_result);
|
NodeOutput blend_output = GetConnectedOutput(kBlendIn);
|
||||||
current_result = post_base_hash;
|
blend_output.node()->Hash(blend_output.output(), hash, time, video_params);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(IsInputConnected(kBlendIn)) {
|
if (!passthrough_blend) {
|
||||||
NodeOutput blend_output = GetConnectedOutput(kBlendIn);
|
NodeOutput base_output = GetConnectedOutput(kBaseIn);
|
||||||
blend_output.node()->Hash(blend_output.output(), hash, time, video_params);
|
base_output.node()->Hash(base_output.output(), hash, time, video_params);
|
||||||
|
}
|
||||||
|
|
||||||
blend_changed_hash = (hash.result() != current_result);
|
Q_ASSERT(!passthrough_base || !passthrough_blend);
|
||||||
}
|
|
||||||
|
|
||||||
if (base_changed_hash && blend_changed_hash) {
|
|
||||||
// Something changed, so we'll add our fingerprint
|
|
||||||
hash.addData(id().toUtf8());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+36
-3
@@ -45,6 +45,32 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const QStrin
|
|||||||
return database;
|
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<TexturePtr>();
|
||||||
|
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)
|
NodeValueTable NodeTraverser::ProcessInput(const Node* node, const QString& input, const TimeRange& range)
|
||||||
{
|
{
|
||||||
// If input is connected, retrieve value directly
|
// If input is connected, retrieve value directly
|
||||||
@@ -134,7 +160,8 @@ QVariant NodeTraverser::ProcessVideoFootage(const FootageJob &stream, const rati
|
|||||||
{
|
{
|
||||||
Q_UNUSED(input_time)
|
Q_UNUSED(input_time)
|
||||||
|
|
||||||
return QVariant::fromValue(stream.video_params());
|
// Create dummy texture with footage params
|
||||||
|
return QVariant::fromValue(std::make_shared<Texture>(stream.video_params()));
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant NodeTraverser::ProcessAudioFootage(const FootageJob& stream, const TimeRange &input_time)
|
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(range)
|
||||||
Q_UNUSED(job)
|
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<Texture>(tex_params));
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant NodeTraverser::ProcessSamples(const Node *node, const TimeRange &range, const SampleJob &job)
|
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(node)
|
||||||
Q_UNUSED(job)
|
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<Texture>(tex_params));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeTraverser::SaveCachedTexture(const QByteArray &hash, const QVariant &texture)
|
void NodeTraverser::SaveCachedTexture(const QByteArray &hash, const QVariant &texture)
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ public:
|
|||||||
video_params_ = params;
|
video_params_ = params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int GetChannelCountFromJob(const GenerateJob& job);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
NodeValueTable ProcessInput(const Node *node, const QString &input, const TimeRange &range);
|
NodeValueTable ProcessInput(const Node *node, const QString &input, const TimeRange &range);
|
||||||
|
|
||||||
|
|||||||
@@ -27,23 +27,29 @@ namespace olive {
|
|||||||
|
|
||||||
class GenerateJob : public AcceleratedJob {
|
class GenerateJob : public AcceleratedJob {
|
||||||
public:
|
public:
|
||||||
|
enum AlphaChannelSetting {
|
||||||
|
kAlphaAuto,
|
||||||
|
kAlphaForceOn,
|
||||||
|
kAlphaForceOff
|
||||||
|
};
|
||||||
|
|
||||||
GenerateJob()
|
GenerateJob()
|
||||||
{
|
{
|
||||||
alpha_channel_required_ = false;
|
alpha_channel_required_ = kAlphaAuto;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetAlphaChannelRequired() const
|
AlphaChannelSetting GetAlphaChannelRequired() const
|
||||||
{
|
{
|
||||||
return alpha_channel_required_;
|
return alpha_channel_required_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetAlphaChannelRequired(bool e)
|
void SetAlphaChannelRequired(AlphaChannelSetting e)
|
||||||
{
|
{
|
||||||
alpha_channel_required_ = e;
|
alpha_channel_required_ = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool alpha_channel_required_;
|
AlphaChannelSetting alpha_channel_required_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -456,22 +456,7 @@ QVariant RenderProcessor::ProcessShader(const Node *node, const TimeRange &range
|
|||||||
|
|
||||||
VideoParams tex_params = ticket_->property("vparam").value<VideoParams>();
|
VideoParams tex_params = ticket_->property("vparam").value<VideoParams>();
|
||||||
|
|
||||||
bool input_textures_have_alpha = false;
|
tex_params.set_channel_count(GetChannelCountFromJob(job));
|
||||||
for (auto it=job.GetValues().cbegin(); it!=job.GetValues().cend(); it++) {
|
|
||||||
if (it.value().type() == NodeValue::kTexture) {
|
|
||||||
TexturePtr tex = it.value().data().value<TexturePtr>();
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
TexturePtr destination = render_ctx_->CreateTexture(tex_params);
|
TexturePtr destination = render_ctx_->CreateTexture(tex_params);
|
||||||
|
|
||||||
@@ -521,12 +506,7 @@ QVariant RenderProcessor::ProcessFrameGeneration(const Node *node, const Generat
|
|||||||
FramePtr frame = Frame::Create();
|
FramePtr frame = Frame::Create();
|
||||||
|
|
||||||
VideoParams frame_params = ticket_->property("vparam").value<VideoParams>();
|
VideoParams frame_params = ticket_->property("vparam").value<VideoParams>();
|
||||||
if (job.GetAlphaChannelRequired()) {
|
frame_params.set_channel_count(GetChannelCountFromJob(job));
|
||||||
frame_params.set_channel_count(VideoParams::kRGBAChannelCount);
|
|
||||||
} else {
|
|
||||||
frame_params.set_channel_count(VideoParams::kRGBChannelCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
frame->set_video_params(frame_params);
|
frame->set_video_params(frame_params);
|
||||||
frame->allocate();
|
frame->allocate();
|
||||||
|
|
||||||
|
|||||||
@@ -28,12 +28,16 @@ const Texture::Interpolation Texture::kDefaultInterpolation = Texture::kMipmappe
|
|||||||
|
|
||||||
Texture::~Texture()
|
Texture::~Texture()
|
||||||
{
|
{
|
||||||
renderer_->DestroyNativeTexture(id_);
|
if (renderer_) {
|
||||||
|
renderer_->DestroyNativeTexture(id_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture::Upload(void *data, int linesize)
|
void Texture::Upload(void *data, int linesize)
|
||||||
{
|
{
|
||||||
renderer_->UploadToTexture(this, data, linesize);
|
if (renderer_) {
|
||||||
|
renderer_->UploadToTexture(this, data, linesize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,19 @@ public:
|
|||||||
|
|
||||||
static const Interpolation kDefaultInterpolation;
|
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) :
|
Texture(Renderer* renderer, const QVariant& native, const VideoParams& param, Type type) :
|
||||||
renderer_(renderer),
|
renderer_(renderer),
|
||||||
params_(param),
|
params_(param),
|
||||||
@@ -67,6 +80,11 @@ public:
|
|||||||
|
|
||||||
void Upload(void* data, int linesize);
|
void Upload(void* data, int linesize);
|
||||||
|
|
||||||
|
bool IsDummy() const
|
||||||
|
{
|
||||||
|
return !renderer_;
|
||||||
|
}
|
||||||
|
|
||||||
int width() const
|
int width() const
|
||||||
{
|
{
|
||||||
return params_.effective_width();
|
return params_.effective_width();
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ function(olive_add_test GROUP NAME SOURCE)
|
|||||||
${NAME}
|
${NAME}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
${CMAKE_SOURCE_DIR}/app
|
${CMAKE_SOURCE_DIR}/app
|
||||||
|
${CMAKE_SOURCE_DIR}/tests
|
||||||
${OLIVE_INCLUDE_DIRS}
|
${OLIVE_INCLUDE_DIRS}
|
||||||
)
|
)
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
@@ -64,4 +65,5 @@ function(olive_add_test GROUP NAME SOURCE)
|
|||||||
add_test(${NAME} ${NAME})
|
add_test(${NAME} ${NAME})
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
add_subdirectory(compositing)
|
||||||
add_subdirectory(timeline)
|
add_subdirectory(timeline)
|
||||||
|
|||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
olive_add_test(Node compositing-tests 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
#include "node/project/sequence/sequence.h"
|
#include "node/project/sequence/sequence.h"
|
||||||
#include "undo/undocommand.h"
|
#include "undo/undocommand.h"
|
||||||
#include "widget/timelinewidget/timelineundo.h"
|
#include "widget/timelinewidget/timelineundo.h"
|
||||||
#include "../testutil.h"
|
#include "testutil.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user