Merge pull request #1875 from ThomasWilshaw/ocio_node

Initial OCIO node implementation
This commit is contained in:
itsmattkc
2022-05-08 11:57:05 -07:00
committed by GitHub
46 changed files with 1486 additions and 118 deletions
+34
View File
@@ -0,0 +1,34 @@
/***
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/>.
***/
#ifndef ALPHAASSOC_H
#define ALPHAASSOC_H
namespace olive {
enum AlphaAssociated {
kAlphaNone,
kAlphaUnassociated,
kAlphaAssociated
};
}
#endif // ALPHAASSOC_H
+21 -12
View File
@@ -26,7 +26,7 @@
namespace olive {
ColorProcessor::ColorProcessor(ColorManager *config, const QString &input, const ColorTransform &transform)
ColorProcessor::ColorProcessor(ColorManager *config, const QString &input, const ColorTransform &transform, Direction direction)
{
QMutexLocker locker(config->mutex());
@@ -41,6 +41,7 @@ ColorProcessor::ColorProcessor(ColorManager *config, const QString &input, const
display_transform->setSrc(input.toUtf8());
display_transform->setDisplay(output.toUtf8());
display_transform->setView(view.toUtf8());
display_transform->setDirection(direction == kNormal ? OCIO::TRANSFORM_DIR_FORWARD : OCIO::TRANSFORM_DIR_INVERSE);
OCIO_SET_C_LOCALE_FOR_SCOPE;
@@ -69,13 +70,25 @@ ColorProcessor::ColorProcessor(ColorManager *config, const QString &input, const
} else {
OCIO_SET_C_LOCALE_FOR_SCOPE;
processor_ = config->GetConfig()->getProcessor(input.toUtf8(),
output.toUtf8());
try {
if (direction == kNormal) {
processor_ = config->GetConfig()->getProcessor(input.toUtf8(), output.toUtf8());
} else {
processor_ = config->GetConfig()->getProcessor(output.toUtf8(), input.toUtf8());
}
} catch (OCIO::Exception &e) {
qWarning() << "ColorProcessor exception:" << e.what();
}
}
cpu_processor_ = processor_->getDefaultCPUProcessor();
id_ = GenerateID(config, input, transform);
}
ColorProcessor::ColorProcessor(OCIO::ConstProcessorRcPtr processor)
{
processor_ = processor;
cpu_processor_ = processor_->getDefaultCPUProcessor();
}
void ColorProcessor::ConvertFrame(Frame *f)
@@ -109,18 +122,14 @@ Color ColorProcessor::ConvertColor(const Color& in)
return Color(c[0], c[1], c[2], c[3]);
}
QString ColorProcessor::GenerateID(ColorManager *config, const QString &input, const ColorTransform &transform)
ColorProcessorPtr ColorProcessor::Create(ColorManager *config, const QString& input, const ColorTransform &transform, Direction direction)
{
return QStringLiteral("%1:%2:%3:%4:%5").arg(config->GetConfigFilename(),
input,
transform.display(),
transform.view(),
transform.look());
return std::make_shared<ColorProcessor>(config, input, transform, direction);
}
ColorProcessorPtr ColorProcessor::Create(ColorManager *config, const QString& input, const ColorTransform &transform)
ColorProcessorPtr ColorProcessor::Create(OCIO::ConstProcessorRcPtr processor)
{
return std::make_shared<ColorProcessor>(config, input, transform);
return std::make_shared<ColorProcessor>(processor);
}
OCIO::ConstProcessorRcPtr ColorProcessor::GetProcessor()
+6 -8
View File
@@ -41,11 +41,13 @@ public:
kInverse
};
ColorProcessor(ColorManager* config, const QString& input, const ColorTransform& dest_space);
ColorProcessor(ColorManager* config, const QString& input, const ColorTransform& dest_space, Direction direction = kNormal);
ColorProcessor(OCIO::ConstProcessorRcPtr processor);
DISABLE_COPY_MOVE(ColorProcessor)
static ColorProcessorPtr Create(ColorManager* config, const QString& input, const ColorTransform& dest_space);
static ColorProcessorPtr Create(ColorManager* config, const QString& input, const ColorTransform& dest_space, Direction direction = kNormal);
static ColorProcessorPtr Create(OCIO::ConstProcessorRcPtr processor);
OCIO::ConstProcessorRcPtr GetProcessor();
@@ -54,20 +56,16 @@ public:
Color ConvertColor(const Color &in);
const QString& id() const
const char *id() const
{
return id_;
return processor_->getCacheID();
}
static QString GenerateID(ColorManager* config, const QString& input, const ColorTransform& dest_space);
private:
OCIO::ConstProcessorRcPtr processor_;
OCIO::ConstCPUProcessorRcPtr cpu_processor_;
QString id_;
};
using ColorProcessorChain = QVector<ColorProcessorPtr>;
+113
View File
@@ -0,0 +1,113 @@
/***
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/>.
***/
#ifndef COLORTRANSFORMJOB_H
#define COLORTRANSFORMJOB_H
#include <QMatrix4x4>
#include <QString>
#include "render/job/generatejob.h"
#include "render/alphaassoc.h"
#include "render/colorprocessor.h"
#include "render/texture.h"
namespace olive {
class Node;
class ColorTransformJob : public GenerateJob
{
public:
ColorTransformJob()
{
processor_ = nullptr;
input_texture_ = nullptr;
custom_shader_src_ = nullptr;
input_alpha_association_ = kAlphaNone;
clear_destination_ = true;
}
QString id() const
{
if (id_.isEmpty()) {
return processor_->id();
} else {
return id_;
}
}
void SetOverrideID(const QString &id) { id_ = id; }
TexturePtr GetInputTexture() const { return input_texture_; }
void SetInputTexture(TexturePtr tex) { input_texture_ = tex; }
ColorProcessorPtr GetColorProcessor() const { return processor_; }
void SetColorProcessor(ColorProcessorPtr p) { processor_ = p; }
const AlphaAssociated &GetInputAlphaAssociation() const { return input_alpha_association_; }
void SetInputAlphaAssociation(const AlphaAssociated &e) { input_alpha_association_ = e; }
const Node *CustomShaderSource() const { return custom_shader_src_; }
const QString &CustomShaderID() const { return custom_shader_id_; }
void SetNeedsCustomShader(const Node *node, const QString &id = QString())
{
custom_shader_src_ = node;
custom_shader_id_ = id;
}
bool IsClearDestinationEnabled() const { return clear_destination_; }
void SetClearDestinationEnabled(bool e) { clear_destination_ = e; }
const QMatrix4x4 &GetTransformMatrix() const { return matrix_; }
void SetTransformMatrix(const QMatrix4x4 &m) { matrix_ = m; }
const QMatrix4x4 &GetCropMatrix() const { return crop_matrix_; }
void SetCropMatrix(const QMatrix4x4 &m) { crop_matrix_ = m; }
const QString &GetFunctionName() const { return function_name_; }
void SetFunctionName(const QString &function_name = QString()) { function_name_ = function_name; };
private:
ColorProcessorPtr processor_;
QString id_;
TexturePtr input_texture_;
const Node *custom_shader_src_;
QString custom_shader_id_;
AlphaAssociated input_alpha_association_;
bool clear_destination_;
QMatrix4x4 matrix_;
QMatrix4x4 crop_matrix_;
QString function_name_;
};
}
Q_DECLARE_METATYPE(olive::ColorTransformJob)
#endif // COLORTRANSFORMJOB_H
+1
View File
@@ -25,6 +25,7 @@
#include <QVector>
#include "generatejob.h"
#include "render/colorprocessor.h"
#include "render/texture.h"
namespace olive {
+34 -36
View File
@@ -52,16 +52,6 @@ TexturePtr Renderer::CreateTexture(const VideoParams &params, const void *data,
return CreateTexture(params, Texture::k2D, data, linesize);
}
void Renderer::BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, AlphaAssociated source_alpha_association, Texture *destination, bool clear_destination, const QMatrix4x4 &matrix, const QMatrix4x4 &crop_matrix)
{
BlitColorManagedInternal(color_processor, source, source_alpha_association, destination, destination->params(), clear_destination, matrix, crop_matrix);
}
void Renderer::BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, AlphaAssociated source_alpha_association, VideoParams params, bool clear_destination, const QMatrix4x4& matrix, const QMatrix4x4 &crop_matrix)
{
BlitColorManagedInternal(color_processor, source, source_alpha_association, nullptr, params, clear_destination, matrix, crop_matrix);
}
TexturePtr Renderer::InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams &params)
{
color_cache_mutex_.lock();
@@ -103,35 +93,45 @@ TexturePtr Renderer::CreateTextureFromNativeHandle(const QVariant &v, const Vide
return std::make_shared<Texture>(this, v, params, type);
}
bool Renderer::GetColorContext(ColorProcessorPtr color_processor, Renderer::ColorContext *ctx)
bool Renderer::GetColorContext(const ColorTransformJob &color_job, Renderer::ColorContext *ctx)
{
QMutexLocker locker(&color_cache_mutex_);
ColorContext& color_ctx = *ctx;
if (color_cache_.contains(color_processor->id())) {
color_ctx = color_cache_.value(color_processor->id());
QString proc_id = color_job.id();
if (color_cache_.contains(proc_id)) {
color_ctx = color_cache_.value(proc_id);
return true;
} else {
// Create shader description
const char* ocio_func_name = "OCIODisplay";
QString ocio_func_name;
if (color_job.GetFunctionName().isEmpty()) {
ocio_func_name = "OCIODisplay";
} else {
ocio_func_name = color_job.GetFunctionName();
}
auto shader_desc = OCIO::GpuShaderDesc::CreateShaderDesc();
shader_desc->setLanguage(OCIO::GPU_LANGUAGE_GLSL_ES_3_0);
shader_desc->setFunctionName(ocio_func_name);
shader_desc->setFunctionName(ocio_func_name.toUtf8());
shader_desc->setResourcePrefix("ocio_");
// Generate shader
color_processor->GetProcessor()->getDefaultGPUProcessor()->extractGpuShaderInfo(shader_desc);
color_job.GetColorProcessor()->GetProcessor()->getDefaultGPUProcessor()->extractGpuShaderInfo(shader_desc);
// Generate shader code using OCIO stub and our auto-generated name
QString shader_frag = FileFunctions::ReadFileAsString(QStringLiteral(":shaders/colormanage.frag")).arg(
shader_desc->getShaderText(),
ocio_func_name
);
ShaderCode code;
if (const Node *shader_src = color_job.CustomShaderSource()) {
// Use shader code from associated node
code = shader_src->GetShaderCode({color_job.CustomShaderID(), shader_desc->getShaderText()});
} else {
// Generate shader code using OCIO stub and our auto-generated name
code = FileFunctions::ReadFileAsString(QStringLiteral(":shaders/colormanage.frag"));
code.set_frag_code(code.frag_code().arg(shader_desc->getShaderText()));
}
// Try to compile shader
color_ctx.compiled_shader = CreateNativeShader(ShaderCode(shader_frag,
FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/default.vert"))));
color_ctx.compiled_shader = CreateNativeShader(code);
if (color_ctx.compiled_shader.isNull()) {
return false;
@@ -199,28 +199,26 @@ bool Renderer::GetColorContext(ColorProcessorPtr color_processor, Renderer::Colo
color_ctx.lut1d_textures[i].interpolation = (interpolation == OCIO::INTERP_NEAREST) ? Texture::kNearest : Texture::kLinear;
}
color_cache_.insert(color_processor->id(), color_ctx);
color_cache_.insert(proc_id, color_ctx);
return true;
}
}
void Renderer::BlitColorManagedInternal(ColorProcessorPtr color_processor, TexturePtr source,
AlphaAssociated source_alpha_association, Texture *destination,
VideoParams params, bool clear_destination, const QMatrix4x4& matrix,
const QMatrix4x4& crop_matrix)
void Renderer::BlitColorManaged(const ColorTransformJob &color_job, Texture *destination, const VideoParams &params)
{
ColorContext color_ctx;
if (!GetColorContext(color_processor, &color_ctx)) {
if (!GetColorContext(color_job, &color_ctx)) {
return;
}
ShaderJob job;
job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(source)));
job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, matrix));
job.InsertValue(QStringLiteral("ove_cropmatrix"), NodeValue(NodeValue::kMatrix, crop_matrix.inverted()));
job.InsertValue(QStringLiteral("ove_maintex_alpha"), NodeValue(NodeValue::kInt, source_alpha_association));
job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(color_job.GetInputTexture())));
job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, color_job.GetTransformMatrix()));
job.InsertValue(QStringLiteral("ove_cropmatrix"), NodeValue(NodeValue::kMatrix, color_job.GetCropMatrix().inverted()));
job.InsertValue(QStringLiteral("ove_maintex_alpha"), NodeValue(NodeValue::kInt, color_job.GetInputAlphaAssociation()));
job.InsertValue(color_job.GetValues());
job.SetAlphaChannelRequired(color_job.GetAlphaChannelRequired());
foreach (const ColorContext::LUT& l, color_ctx.lut3d_textures) {
job.InsertValue(l.name, NodeValue(NodeValue::kTexture, QVariant::fromValue(l.texture)));
@@ -232,9 +230,9 @@ void Renderer::BlitColorManagedInternal(ColorProcessorPtr color_processor, Textu
}
if (destination) {
BlitToTexture(color_ctx.compiled_shader, job, destination, clear_destination);
BlitToTexture(color_ctx.compiled_shader, job, destination, color_job.IsClearDestinationEnabled());
} else {
Blit(color_ctx.compiled_shader, job, params, clear_destination);
Blit(color_ctx.compiled_shader, job, params, color_job.IsClearDestinationEnabled());
}
}
+11 -14
View File
@@ -29,6 +29,7 @@
#include "common/timerange.h"
#include "node/node.h"
#include "render/colorprocessor.h"
#include "render/job/colortransformjob.h"
#include "render/videoparams.h"
#include "texture.h"
@@ -63,14 +64,15 @@ public:
Blit(shader, job, nullptr, params, clear_destination);
}
enum AlphaAssociated {
kAlphaNone,
kAlphaUnassociated,
kAlphaAssociated
};
void BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, AlphaAssociated source_alpha_association, Texture* destination, bool clear_destination = true, const QMatrix4x4& matrix = QMatrix4x4(), const QMatrix4x4 &crop_matrix = QMatrix4x4());
void BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, AlphaAssociated source_alpha_association, VideoParams params, bool clear_destination = true, const QMatrix4x4& matrix = QMatrix4x4(), const QMatrix4x4 &crop_matrix = QMatrix4x4());
void BlitColorManaged(const ColorTransformJob &color_job, Texture* destination, const VideoParams &params);
void BlitColorManaged(const ColorTransformJob &job, Texture* destination)
{
BlitColorManaged(job, destination, destination->params());
}
void BlitColorManaged(const ColorTransformJob &job, const VideoParams &params)
{
BlitColorManaged(job, nullptr, params);
}
TexturePtr InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams &params);
@@ -126,12 +128,7 @@ private:
};
bool GetColorContext(ColorProcessorPtr color_processor, ColorContext* ctx);
void BlitColorManagedInternal(ColorProcessorPtr color_processor, TexturePtr source,
AlphaAssociated source_alpha_association,
Texture* destination, VideoParams params, bool clear_destination,
const QMatrix4x4 &matrix, const QMatrix4x4 &crop_matrix);
bool GetColorContext(const ColorTransformJob &color_job, ColorContext* ctx);
QHash<QString, ColorContext> color_cache_;
+30 -11
View File
@@ -103,9 +103,14 @@ FramePtr RenderProcessor::GenerateFrame(TexturePtr texture, const rational& time
if (output_color_transform) {
// Yes color transform, blit color managed
render_ctx_->BlitColorManaged(output_color_transform, texture,
OLIVE_CONFIG("ReassocLinToNonLin").toBool() ? Renderer::kAlphaAssociated : Renderer::kAlphaNone,
blit_tex.get(), true, matrix);
ColorTransformJob job;
job.SetColorProcessor(output_color_transform);
job.SetInputTexture(texture);
job.SetInputAlphaAssociation(OLIVE_CONFIG("ReassocLinToNonLin").toBool() ? kAlphaAssociated : kAlphaNone);
job.SetTransformMatrix(matrix);
render_ctx_->BlitColorManaged(job, blit_tex.get());
} else {
// No color transform, just blit
ShaderJob job;
@@ -459,19 +464,21 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ
using_colorspace,
color_manager->GetReferenceColorSpace());
Renderer::AlphaAssociated alpha_assoc;
ColorTransformJob job;
job.SetColorProcessor(processor);
job.SetInputTexture(unmanaged_texture);
if (stream_data.channel_count() != VideoParams::kRGBAChannelCount
|| stream_data.colorspace() == color_manager->GetReferenceColorSpace()) {
alpha_assoc = Renderer::kAlphaNone;
job.SetInputAlphaAssociation(kAlphaNone);
} else if (stream_data.premultiplied_alpha()) {
alpha_assoc = Renderer::kAlphaAssociated;
job.SetInputAlphaAssociation(kAlphaAssociated);
} else {
alpha_assoc = Renderer::kAlphaUnassociated;
job.SetInputAlphaAssociation(kAlphaUnassociated);
}
render_ctx_->BlitColorManaged(processor, unmanaged_texture,
alpha_assoc,
destination.get());
render_ctx_->BlitColorManaged(job, destination.get());
}
}
}
@@ -550,6 +557,11 @@ void RenderProcessor::ProcessSamples(SampleBufferPtr destination, const Node *no
}
}
void RenderProcessor::ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob &job)
{
render_ctx_->BlitColorManaged(job, destination.get());
}
void RenderProcessor::ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob &job)
{
FramePtr frame = Frame::Create();
@@ -571,7 +583,14 @@ void RenderProcessor::ConvertToReferenceSpace(TexturePtr destination, TexturePtr
{
ColorManager* color_manager = Node::ValueToPtr<ColorManager>(ticket_->property("colormanager"));
ColorProcessorPtr cp = ColorProcessor::Create(color_manager, input_cs, color_manager->GetReferenceColorSpace());
render_ctx_->BlitColorManaged(cp, source, Renderer::kAlphaAssociated, destination.get());
ColorTransformJob ctj;
ctj.SetColorProcessor(cp);
ctj.SetInputTexture(source);
ctj.SetInputAlphaAssociation(kAlphaAssociated);
render_ctx_->BlitColorManaged(ctj, destination.get());
}
}
+2
View File
@@ -52,6 +52,8 @@ protected:
virtual void ProcessSamples(SampleBufferPtr destination, const Node *node, const TimeRange &range, const SampleJob &job) override;
virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob& job) override;
virtual void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob& job) override;
virtual bool CanCacheFrames() override;
+4 -8
View File
@@ -33,15 +33,11 @@ public:
{
}
const QString& frag_code() const
{
return frag_code_;
}
const QString& frag_code() const { return frag_code_; }
void set_frag_code(const QString &f) { frag_code_ = f; }
const QString& vert_code() const
{
return vert_code_;
}
const QString& vert_code() const { return vert_code_; }
void set_vert_code(const QString &v) { vert_code_ = v; }
private:
QString frag_code_;