Merge branch 'master' into track-arrays

This commit is contained in:
itsmattkc
2019-12-06 19:42:26 +11:00
41 changed files with 543 additions and 312 deletions
+2 -2
View File
@@ -283,7 +283,7 @@ void NodeInput::set_maximum(const QVariant &max)
has_maximum_ = true;
}
void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_connections)
void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_connections, bool lock_connections)
{
Q_ASSERT(source->id() == dest->id());
@@ -295,7 +295,7 @@ void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_conn
// Copy connections
if (include_connections && source->get_connected_output() != nullptr) {
ConnectEdge(source->get_connected_output(), dest);
ConnectEdge(source->get_connected_output(), dest, lock_connections);
}
// If these inputs are an array, copy the subparams too
+1 -1
View File
@@ -121,7 +121,7 @@ public:
/**
* @brief Copy all values including keyframe information and connections from another NodeInput
*/
static void CopyValues(NodeInput* source, NodeInput* dest, bool include_connections = true);
static void CopyValues(NodeInput* source, NodeInput* dest, bool include_connections = true, bool lock_connections = true);
signals:
void ValueChanged(const rational& start, const rational& end);
+1 -1
View File
@@ -182,7 +182,7 @@ void Node::CopyInputs(Node *source, Node *destination, bool include_connections)
if (src->dependent()) {
NodeInput* dst = static_cast<NodeInput*>(dst_param.at(i));
NodeInput::CopyValues(src, dst, include_connections);
NodeInput::CopyValues(src, dst, include_connections, false);
}
}
}
+9 -5
View File
@@ -103,7 +103,7 @@ void NodeParam::DisconnectAll()
}
}
NodeEdgePtr NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input)
NodeEdgePtr NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input, bool lock)
{
// If the input can only accept one input (the default) and has one already, disconnect it
DisconnectForNewOutput(input);
@@ -127,14 +127,18 @@ NodeEdgePtr NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input)
// that's difficult to diagnose. This makes that issue very clear.
Q_ASSERT(output->parentNode() != input->parentNode());
output->parentNode()->LockUserInput();
input->parentNode()->LockUserInput();
if (lock) {
output->parentNode()->LockUserInput();
input->parentNode()->LockUserInput();
}
output->edges_.append(edge);
input->edges_.append(edge);
output->parentNode()->UnlockUserInput();
input->parentNode()->UnlockUserInput();
if (lock) {
output->parentNode()->UnlockUserInput();
input->parentNode()->UnlockUserInput();
}
// Emit a signal than an edge was added (only one signal needs emitting)
emit input->EdgeAdded(edge);
+1 -1
View File
@@ -292,7 +292,7 @@ public:
*
* This function emits EdgeAdded().
*/
static NodeEdgePtr ConnectEdge(NodeOutput *output, NodeInput *input);
static NodeEdgePtr ConnectEdge(NodeOutput *output, NodeInput *input, bool lock = true);
/**
* @brief Disconnect an edge
+8 -8
View File
@@ -35,7 +35,7 @@ Footage::~Footage()
ClearStreams();
}
const Footage::Status& Footage::status()
const Footage::Status& Footage::status() const
{
return status_;
}
@@ -56,7 +56,7 @@ void Footage::Clear()
set_status(kUnprobed);
}
const QString &Footage::filename()
const QString &Footage::filename() const
{
return filename_;
}
@@ -66,7 +66,7 @@ void Footage::set_filename(const QString &s)
filename_ = s;
}
const QDateTime &Footage::timestamp()
const QDateTime &Footage::timestamp() const
{
return timestamp_;
}
@@ -85,17 +85,17 @@ void Footage::add_stream(StreamPtr s)
streams_.last()->set_footage(this);
}
StreamPtr Footage::stream(int index)
StreamPtr Footage::stream(int index) const
{
return streams_.at(index);
}
const QList<StreamPtr> &Footage::streams()
const QList<StreamPtr> &Footage::streams() const
{
return streams_;
}
int Footage::stream_count()
int Footage::stream_count() const
{
return streams_.size();
}
@@ -105,7 +105,7 @@ Item::Type Footage::type() const
return kFootage;
}
const QString &Footage::decoder()
const QString &Footage::decoder() const
{
return decoder_;
}
@@ -139,7 +139,7 @@ QIcon Footage::icon()
return olive::icon::Image;
}
/* fall through */
/* fall-through */
case kInvalid:
return olive::icon::Error;
}
+9 -26
View File
@@ -24,6 +24,7 @@
#include <QList>
#include <QDateTime>
#include "common/constructors.h"
#include "common/rational.h"
#include "project/item/item.h"
#include "project/item/footage/audiostream.h"
@@ -59,25 +60,7 @@ public:
*/
virtual ~Footage() override;
/**
* @brief Deleted copy constructor
*/
Footage(const Footage& other) = delete;
/**
* @brief Deleted move constructor
*/
Footage(Footage&& other) = delete;
/**
* @brief Deleted copy assignment
*/
Footage& operator=(const Footage& other) = delete;
/**
* @brief Deleted move assignment
*/
Footage& operator=(Footage&& other) = delete;
DISABLE_COPY_MOVE(Footage)
/**
* @brief Check the ready state of this Footage object
@@ -86,7 +69,7 @@ public:
*
* If the Footage has been successfully probed, this will return TRUE.
*/
const Status& status();
const Status& status() const;
/**
* @brief Set ready state
@@ -114,7 +97,7 @@ public:
/**
* @brief Return the current filename of this Footage object
*/
const QString& filename();
const QString& filename() const;
/**
* @brief Set the filename
@@ -134,7 +117,7 @@ public:
* The file's last modified timestamp is stored for potential organization in the ProjectExplorer. It can be
* retrieved here.
*/
const QDateTime& timestamp();
const QDateTime& timestamp() const;
/**
* @brief Set the last modified time/date
@@ -172,17 +155,17 @@ public:
*
* The stream at the index provided
*/
StreamPtr stream(int index);
StreamPtr stream(int index) const;
/**
* @brief Returns a list of the streams in this Footage
*/
const QList<StreamPtr>& streams();
const QList<StreamPtr>& streams() const;
/**
* @brief Retrieve total number of streams in this Footage file
*/
int stream_count();
int stream_count() const;
/**
* @brief Item::Type() override
@@ -198,7 +181,7 @@ public:
*
* A decoder ID
*/
const QString& decoder();
const QString& decoder() const;
/**
* @brief Used by decoders when they Probe to attach itself to this Footage
+12
View File
@@ -20,6 +20,7 @@
#include "stream.h"
#include "footage.h"
#include "ui/icons/icons.h"
Stream::Stream() :
@@ -114,3 +115,14 @@ QIcon Stream::IconFromType(const Stream::Type &type)
return QIcon();
}
StreamID Stream::ToID() const
{
return StreamID(footage_->filename(), index_);
}
StreamID::StreamID(const QString &filename, const int &stream_index) :
filename_(filename),
stream_index_(stream_index)
{
}
+13
View File
@@ -29,6 +29,17 @@
class Footage;
class StreamID {
public:
StreamID(const QString& filename, const int& stream_index);
private:
QString filename_;
int stream_index_;
};
/**
* @brief A base class for keeping metadata about a media stream.
*
@@ -84,6 +95,8 @@ public:
static QIcon IconFromType(const Type& type);
StreamID ToID() const;
private:
Footage* footage_;
+4 -3
View File
@@ -21,9 +21,6 @@ add_subdirectory(vulkan)
set(OLIVE_SOURCES
${OLIVE_SOURCES}
render/backend/decodercache.h
render/backend/decodercache.cpp
render/backend/renderbackend.h
render/backend/renderbackend.cpp
render/backend/renderworker.h
@@ -40,6 +37,10 @@ set(OLIVE_SOURCES
render/backend/videorenderframecache.cpp
render/backend/videorenderworker.h
render/backend/videorenderworker.cpp
render/backend/rendercache.h
render/backend/colorprocessorcache.h
render/backend/decodercache.h
PARENT_SCOPE
)
+2 -2
View File
@@ -37,7 +37,7 @@ bool AudioBackend::InitInternal()
// Initiate one thread per CPU core
for (int i=0;i<threads().size();i++) {
// Create one processor object for each thread
AudioWorker* processor = new AudioWorker(decoder_cache());
AudioWorker* processor = new AudioWorker();
processor->SetParameters(params());
processors_.append(processor);
}
@@ -67,7 +67,7 @@ void AudioBackend::ConnectWorkerToThis(RenderWorker *worker)
void AudioBackend::ThreadCompletedCache(NodeDependency dep, NodeValueTable data)
{
caching_ = false;
SetWorkerBusyState(static_cast<RenderWorker*>(sender()), false);
QByteArray cached_samples = data.Get(NodeParam::kSamples).toByteArray();
+5 -3
View File
@@ -1,11 +1,13 @@
#include "audioworker.h"
AudioWorker::AudioWorker(DecoderCache *decoder_cache, QObject *parent) :
AudioRenderWorker(decoder_cache, parent)
AudioWorker::AudioWorker(QObject *parent) :
AudioRenderWorker(parent)
{
}
void AudioWorker::FrameToValue(FramePtr frame, NodeValueTable *table)
void AudioWorker::FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable *table)
{
Q_UNUSED(stream)
table->Push(NodeParam::kSamples, frame->ToByteArray());
}
+2 -2
View File
@@ -6,10 +6,10 @@
class AudioWorker : public AudioRenderWorker
{
public:
AudioWorker(DecoderCache* decoder_cache, QObject* parent = nullptr);
AudioWorker(QObject* parent = nullptr);
protected:
virtual void FrameToValue(FramePtr frame, NodeValueTable* table) override;
virtual void FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable* table) override;
private:
@@ -29,6 +29,8 @@ void AudioRenderBackend::SetParameters(const AudioRenderingParams &params)
void AudioRenderBackend::InvalidateCache(const rational &start_range, const rational &end_range)
{
RenderBackend::InvalidateCache(start_range, end_range);
rational start_range_adj = qMax(rational(0), start_range);
rational end_range_adj = qMin(SequenceLength(), end_range);
+2 -2
View File
@@ -2,8 +2,8 @@
#include "audio/audiomanager.h"
AudioRenderWorker::AudioRenderWorker(DecoderCache *decoder_cache, QObject *parent) :
RenderWorker(decoder_cache, parent)
AudioRenderWorker::AudioRenderWorker(QObject *parent) :
RenderWorker(parent)
{
}
+1 -1
View File
@@ -7,7 +7,7 @@ class AudioRenderWorker : public RenderWorker
{
Q_OBJECT
public:
AudioRenderWorker(DecoderCache* decoder_cache, QObject* parent = nullptr);
AudioRenderWorker(QObject* parent = nullptr);
void SetParameters(const AudioRenderingParams& audio_params);
+10
View File
@@ -0,0 +1,10 @@
#ifndef COLORPROCESSORCACHE_H
#define COLORPROCESSORCACHE_H
#include "project/item/footage/stream.h"
#include "render/colorprocessor.h"
#include "rendercache.h"
using ColorProcessorCache = RenderCache<Stream*, ColorProcessorPtr>;
#endif // COLORPROCESSORCACHE_H
-21
View File
@@ -1,21 +0,0 @@
#include "decodercache.h"
DecoderCache::DecoderCache()
{
}
void DecoderCache::Clear()
{
decoders_.clear();
}
void DecoderCache::AddDecoder(Stream *stream, DecoderPtr shader)
{
decoders_.insert(stream, shader);
}
DecoderPtr DecoderCache::GetDecoder(Stream *stream)
{
return decoders_.value(stream);
}
+2 -18
View File
@@ -3,24 +3,8 @@
#include "decoder/decoder.h"
#include "project/item/footage/stream.h"
#include "rendercache.h"
/**
* @brief Thread-safe cache of decoders
*/
class DecoderCache
{
public:
DecoderCache();
void Clear();
void AddDecoder(Stream* stream, DecoderPtr shader);
DecoderPtr GetDecoder(Stream* stream);
private:
QMap<Stream*, DecoderPtr> decoders_;
};
using DecoderCache = ThreadSafeRenderCache<Stream*, DecoderPtr>;
#endif // DECODERCACHE_H
+2 -1
View File
@@ -20,12 +20,13 @@ set(OLIVE_SOURCES
render/backend/opengl/functions.cpp
render/backend/opengl/openglbackend.h
render/backend/opengl/openglbackend.cpp
render/backend/opengl/openglcolorprocessor.h
render/backend/opengl/openglcolorprocessor.cpp
render/backend/opengl/openglframebuffer.h
render/backend/opengl/openglframebuffer.cpp
render/backend/opengl/openglshader.h
render/backend/opengl/openglshader.cpp
render/backend/opengl/openglshadercache.h
render/backend/opengl/openglshadercache.cpp
render/backend/opengl/opengltexture.h
render/backend/opengl/opengltexture.cpp
render/backend/opengl/openglworker.h
+29 -23
View File
@@ -6,7 +6,8 @@
#include "functions.h"
OpenGLBackend::OpenGLBackend(QObject *parent) :
VideoRenderBackend(parent)
VideoRenderBackend(parent),
last_download_thread_(0)
{
}
@@ -31,7 +32,7 @@ bool OpenGLBackend::InitInternal()
// Initiate one thread per CPU core
for (int i=0;i<threads().size();i++) {
// Create one processor object for each thread
OpenGLWorker* processor = new OpenGLWorker(share_ctx, &shader_cache_, decoder_cache(), frame_cache());
OpenGLWorker* processor = new OpenGLWorker(share_ctx, &shader_cache_, frame_cache());
processor->SetParameters(params());
processors_.append(processor);
}
@@ -76,14 +77,14 @@ bool OpenGLBackend::CompileInternal()
foreach (Node* n, nodes) {
// Check if we have a shader or not
if (!shader_cache_.HasShader(n)) {
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()) {
// We enter a null shader so we don't try to compile this again
shader_cache_.AddShader(n, nullptr);
shader_cache_.Add(n->id(), nullptr);
} else {
// Since we have shader code, compile it now
OpenGLShaderPtr program;
@@ -113,7 +114,7 @@ bool OpenGLBackend::CompileInternal()
return false;
}
shader_cache_.AddShader(n, program);
shader_cache_.Add(n->id(), program);
//qDebug() << "Compiled" << connected_output->parent()->id() << "->" << connected_output->id();
}
@@ -135,7 +136,7 @@ bool OpenGLBackend::TimeIsCached(const TimeRange &time)
void OpenGLBackend::ThreadCompletedFrame(NodeDependency path, QByteArray hash, NodeValueTable table)
{
caching_ = false;
SetWorkerBusyState(static_cast<RenderWorker*>(sender()), false);
QVariant value = table.Get(NodeParam::kTexture);
OpenGLTexturePtr texture = value.value<OpenGLTexturePtr>();
@@ -148,19 +149,14 @@ void OpenGLBackend::ThreadCompletedFrame(NodeDependency path, QByteArray hash, N
QString cache_fn = frame_cache()->CachePathName(hash);
// Find an available worker to download this texture
foreach (RenderWorker* worker, processors_) {
// Check if one is available, but worst case if none of them are available, just queue it on the last worker since
// it's the least likely to get work
if (worker->IsAvailable() || worker == processors_.last()) {
QMetaObject::invokeMethod(worker,
"Download",
Q_ARG(NodeDependency, path),
Q_ARG(QByteArray, hash),
Q_ARG(QVariant, QVariant::fromValue(texture)),
Q_ARG(QString, cache_fn));
break;
}
}
QMetaObject::invokeMethod(processors_.at(last_download_thread_%processors_.size()),
"Download",
Q_ARG(NodeDependency, path),
Q_ARG(QByteArray, hash),
Q_ARG(QVariant, QVariant::fromValue(texture)),
Q_ARG(QString, cache_fn));
last_download_thread_++;
}
// Set as push texture
@@ -168,6 +164,7 @@ void OpenGLBackend::ThreadCompletedFrame(NodeDependency path, QByteArray hash, N
emit CachedFrameReady(path.in(), value);
}
// Queue up a new frame for this worker
CacheNext();
}
@@ -175,18 +172,27 @@ void OpenGLBackend::ThreadCompletedDownload(NodeDependency dep, QByteArray hash)
{
frame_cache()->SetHash(dep.in(), hash);
emit CachedTimeReady(dep.in());
// Emit for each frame that has this hash (some may have been added in ThreadSkippedFrame)
QList<rational> times_with_this_hash = frame_cache()->TimesWithHash(hash);
foreach (const rational& t, times_with_this_hash) {
emit CachedTimeReady(t);
}
}
void OpenGLBackend::ThreadSkippedFrame()
void OpenGLBackend::ThreadSkippedFrame(NodeDependency dep, QByteArray hash)
{
caching_ = false;
frame_cache()->SetHash(dep.in(), hash);
SetWorkerBusyState(static_cast<RenderWorker*>(sender()), false);
// Queue up a new frame for this worker
CacheNext();
}
void OpenGLBackend::ThreadHashAlreadyExists(NodeDependency dep, QByteArray hash)
{
ThreadCompletedDownload(dep, hash);
SetWorkerBusyState(static_cast<RenderWorker*>(sender()), false);
ThreadSkippedFrame();
// Queue up a new frame for this worker
CacheNext();
}
+3 -1
View File
@@ -34,10 +34,12 @@ private:
OpenGLShaderCache shader_cache_;
int last_download_thread_;
private slots:
void ThreadCompletedFrame(NodeDependency path, QByteArray hash, NodeValueTable table);
void ThreadCompletedDownload(NodeDependency dep, QByteArray hash);
void ThreadSkippedFrame();
void ThreadSkippedFrame(NodeDependency dep, QByteArray hash);
void ThreadHashAlreadyExists(NodeDependency dep, QByteArray hash);
};
@@ -0,0 +1,66 @@
#include "openglcolorprocessor.h"
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include "functions.h"
void OpenGLColorProcessor::Enable(QOpenGLContext *context, bool alpha_is_associated)
{
if (IsEnabled()) {
return;
}
context_ = context;
pipeline_ = OpenGLShader::CreateOCIO(context_,
ocio_lut_,
GetProcessor(),
alpha_is_associated);
}
bool OpenGLColorProcessor::IsEnabled() const
{
return ocio_lut_;
}
OpenGLShaderPtr OpenGLColorProcessor::pipeline() const
{
return pipeline_;
}
void OpenGLColorProcessor::ProcessOpenGL()
{
olive::gl::OCIOBlit(pipeline_, ocio_lut_);
}
OpenGLColorProcessor::OpenGLColorProcessor(const QString &source_space, const QString &dest_space) :
ColorProcessor(source_space, dest_space),
ocio_lut_(0)
{
}
OpenGLColorProcessor::OpenGLColorProcessor(const QString &source_space, QString display, QString view, const QString &look) :
ColorProcessor(source_space, display, view, look),
ocio_lut_(0)
{
}
OpenGLColorProcessor::~OpenGLColorProcessor()
{
if (IsEnabled()) {
// Clean up OCIO LUT texture and shader
context_->functions()->glDeleteTextures(1, &ocio_lut_);
pipeline_ = nullptr;
}
}
OpenGLColorProcessorPtr OpenGLColorProcessor::CreateOpenGL(const QString &source_space, const QString &dest_space)
{
return std::make_shared<OpenGLColorProcessor>(source_space, dest_space);
}
OpenGLColorProcessorPtr OpenGLColorProcessor::CreateOpenGL(const QString &source_space, const QString &display, const QString &view, const QString &look)
{
return std::make_shared<OpenGLColorProcessor>(source_space, display, view, look);
}
@@ -0,0 +1,45 @@
#ifndef OPENGLCOLORPROCESSOR_H
#define OPENGLCOLORPROCESSOR_H
#include "openglshader.h"
#include "render/colorprocessor.h"
class OpenGLColorProcessor;
using OpenGLColorProcessorPtr = std::shared_ptr<OpenGLColorProcessor>;
class OpenGLColorProcessor : public ColorProcessor
{
public:
OpenGLColorProcessor(const QString &source_space, const QString &dest_space);
OpenGLColorProcessor(const QString& source_space,
QString display,
QString view,
const QString& look);
~OpenGLColorProcessor();
static OpenGLColorProcessorPtr CreateOpenGL(const QString& source_space, const QString& dest_space);
static OpenGLColorProcessorPtr CreateOpenGL(const QString& source_space,
const QString& display,
const QString& view,
const QString& look);
void Enable(QOpenGLContext* context, bool alpha_is_associated);
bool IsEnabled() const;
OpenGLShaderPtr pipeline() const;
void ProcessOpenGL();
private:
QOpenGLContext* context_;
GLuint ocio_lut_;
OpenGLShaderPtr pipeline_;
};
#endif // OPENGLCOLORPROCESSOR_H
@@ -1,28 +0,0 @@
#include "openglshadercache.h"
#include "node/node.h"
void OpenGLShaderCache::Clear()
{
compiled_nodes_.clear();
}
void OpenGLShaderCache::AddShader(Node *output, OpenGLShaderPtr shader)
{
compiled_nodes_.insert(GenerateShaderID(output), shader);
}
OpenGLShaderPtr OpenGLShaderCache::GetShader(Node *output)
{
return compiled_nodes_.value(GenerateShaderID(output));
}
QString OpenGLShaderCache::GenerateShaderID(Node *output)
{
return output->id();
}
bool OpenGLShaderCache::HasShader(Node *output)
{
return compiled_nodes_.contains(GenerateShaderID(output));
}
+3 -24
View File
@@ -1,32 +1,11 @@
#ifndef OPENGLSHADERCACHE_H
#define OPENGLSHADERCACHE_H
#include <QMutex>
#include <QString>
#include "node/output.h"
#include "openglshader.h"
#include "render/backend/rendercache.h"
/**
* @brief Thread-safe cache of OpenGL shaders
*/
class OpenGLShaderCache
{
public:
OpenGLShaderCache() = default;
void Clear();
void AddShader(Node* output, OpenGLShaderPtr shader);
OpenGLShaderPtr GetShader(Node* output);
bool HasShader(Node* output);
private:
QString GenerateShaderID(Node *output);
QMap<QString, OpenGLShaderPtr> compiled_nodes_;
};
using OpenGLShaderCache = RenderCache<QString, OpenGLShaderPtr>;
#endif // OPENGLSHADERCACHE_H
+78 -12
View File
@@ -1,11 +1,14 @@
#include "openglworker.h"
#include "core.h"
#include "functions.h"
#include "node/node.h"
#include "openglcolorprocessor.h"
#include "render/colormanager.h"
#include "render/pixelservice.h"
OpenGLWorker::OpenGLWorker(QOpenGLContext *share_ctx, OpenGLShaderCache *shader_cache, DecoderCache *decoder_cache, VideoRenderFrameCache *frame_cache, QObject *parent) :
VideoRenderWorker(decoder_cache, frame_cache, parent),
OpenGLWorker::OpenGLWorker(QOpenGLContext *share_ctx, OpenGLShaderCache *shader_cache, VideoRenderFrameCache *frame_cache, QObject *parent) :
VideoRenderWorker(frame_cache, parent),
share_ctx_(share_ctx),
ctx_(nullptr),
functions_(nullptr),
@@ -47,18 +50,79 @@ bool OpenGLWorker::InitInternal()
return true;
}
void OpenGLWorker::FrameToValue(FramePtr frame, NodeValueTable *table)
void OpenGLWorker::FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable *table)
{
// Set up OCIO context
OpenGLColorProcessorPtr color_processor = std::static_pointer_cast<OpenGLColorProcessor>(color_cache()->Get(stream.get()));
// Ensure stream is video or image type
if (stream->type() != Stream::kVideo && stream->type() != Stream::kImage) {
return;
}
ImageStreamPtr video_stream = std::static_pointer_cast<ImageStream>(stream);
if (!color_processor) {
QString input_colorspace = video_stream->colorspace();
if (input_colorspace.isEmpty()) {
// FIXME: Should use Stream->Footage to find the Project* since that's a direct chain
input_colorspace = olive::core.GetActiveProject()->default_input_colorspace();
}
color_processor = OpenGLColorProcessor::CreateOpenGL(input_colorspace,
OCIO::ROLE_SCENE_LINEAR);
color_cache()->Add(stream.get(), color_processor);
}
// OCIO's CPU conversion is more accurate, so for online we render on CPU but offline we render GPU
if (video_params().mode() == olive::kOnline) {
// If alpha is associated, disassociate for the color transform
if (video_stream->premultiplied_alpha()) {
ColorManager::DisassociateAlpha(frame);
}
// Convert frame to float for OCIO
frame = PixelService::ConvertPixelFormat(frame, olive::PIX_FMT_RGBA32F);
// Perform color transform
color_processor->ConvertFrame(frame);
// Associate alpha
if (video_stream->premultiplied_alpha()) {
ColorManager::ReassociateAlpha(frame);
} else {
ColorManager::AssociateAlpha(frame);
}
}
OpenGLTexturePtr footage_tex = std::make_shared<OpenGLTexture>();
footage_tex->Create(ctx_, frame);
// OCIO's CPU conversion is more accurate, so for online we render on CPU but offline we render GPU
//if (video_params().mode() == olive::kOnline) {
// Convert frame to float for
//frame = PixelService::ConvertPixelFormat(frame, olive::PIX_FMT_RGBA32F);
//}
if (video_params().mode() == olive::kOffline) {
if (!color_processor->IsEnabled()) {
color_processor->Enable(ctx_, video_stream->premultiplied_alpha());
}
// FIXME: Alpha association and color management
// Create destination texture
OpenGLTexturePtr associated_tex = std::make_shared<OpenGLTexture>();
associated_tex->Create(ctx_, footage_tex->width(), footage_tex->height(), footage_tex->format());
// Set viewport for texture size
functions_->glViewport(0, 0, footage_tex->width(), footage_tex->height());
buffer_.Attach(associated_tex);
buffer_.Bind();
footage_tex->Bind();
// Blit old texture to new texture through OCIO shader
color_processor->ProcessOpenGL();
footage_tex->Release();
buffer_.Release();
buffer_.Detach();
footage_tex = associated_tex;
}
table->Push(NodeParam::kTexture, QVariant::fromValue(footage_tex));
}
@@ -66,7 +130,6 @@ void OpenGLWorker::FrameToValue(FramePtr frame, NodeValueTable *table)
void OpenGLWorker::CloseInternal()
{
buffer_.Destroy();
functions_ = nullptr;
delete ctx_;
}
@@ -80,7 +143,7 @@ void OpenGLWorker::ParametersChangedEvent()
void OpenGLWorker::RunNodeAccelerated(Node *node, const NodeValueDatabase *input_params, NodeValueTable *output_params)
{
OpenGLShaderPtr shader = shader_cache_->GetShader(node);
OpenGLShaderPtr shader = shader_cache_->Get(node->id());
if (!shader) {
return;
@@ -184,7 +247,10 @@ void OpenGLWorker::RunNodeAccelerated(Node *node, const NodeValueDatabase *input
}
}
//qDebug() << " Blitting with shader!";
// Ensure viewport is correct
functions_->glViewport(0, 0, video_params().effective_width(), video_params().effective_height());
// Blit this texture through this shader
olive::gl::Blit(shader);
// Release any textures we bound before
+1 -2
View File
@@ -13,7 +13,6 @@ class OpenGLWorker : public VideoRenderWorker {
public:
OpenGLWorker(QOpenGLContext* share_ctx,
OpenGLShaderCache* shader_cache,
DecoderCache* decoder_cache,
VideoRenderFrameCache* frame_cache,
QObject* parent = nullptr);
@@ -45,7 +44,7 @@ protected:
virtual void CloseInternal() override;
virtual void FrameToValue(FramePtr frame, NodeValueTable* table) override;
virtual void FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable* table) override;
virtual void RunNodeAccelerated(Node *node, const NodeValueDatabase *input_params, NodeValueTable* output_params) override;
+78 -53
View File
@@ -6,12 +6,12 @@
RenderBackend::RenderBackend(QObject *parent) :
QObject(parent),
compiled_(false),
caching_(false),
started_(false),
viewer_node_(nullptr),
copied_viewer_node_(nullptr),
value_update_queued_(false),
recompile_queued_(false)
recompile_queued_(false),
input_update_queued_(false)
{
}
@@ -53,17 +53,22 @@ void RenderBackend::Close()
CloseInternal();
decoder_cache_.Clear();
for (int i=0;i<processors_.size();i++) {
// Invoke close and quit signals on processor and thred
QMetaObject::invokeMethod(processors_.at(i),
"Close",
Qt::QueuedConnection);
foreach (QThread* thread, threads_) {
thread->quit();
thread->wait(); // FIXME: Maximum time in case a thread is stuck?
threads_.at(i)->quit();
}
for (int i=0;i<processors_.size();i++) {
threads_.at(i)->wait(); // FIXME: Maximum time in case a thread is stuck?
delete threads_.at(i);
delete processors_.at(i);
}
threads_.clear();
foreach (RenderWorker* processor, processors_) {
delete processor;
}
processors_.clear();
}
@@ -100,12 +105,17 @@ bool RenderBackend::IsInitiated()
return started_;
}
void RenderBackend::InvalidateCache(const rational &start_range, const rational &end_range)
{
Q_UNUSED(start_range)
Q_UNUSED(end_range)
input_update_queued_ = true;
}
bool RenderBackend::Compile()
{
if (recompile_queued_) {
Decompile();
recompile_queued_ = false;
} else if (compiled_) {
if (compiled_) {
return true;
}
@@ -199,39 +209,50 @@ void RenderBackend::DisconnectViewer(ViewerOutput *node)
void RenderBackend::CacheNext()
{
if (!Init() || cache_queue_.isEmpty() || !ViewerIsConnected() || caching_) {
if (!Init()
|| cache_queue_.isEmpty()
|| !ViewerIsConnected()) {
return;
}
UpdateNodeInputs();
TimeRange cache_frame = cache_queue_.takeFirst();
//qDebug() << "Caching" << cache_frame.in();
caching_ = GenerateData(cache_frame);
}
bool RenderBackend::GenerateData(const TimeRange &range)
{
if (!Compile()) {
qDebug() << "Graph remains uncompiled, nothing to be done";
return false;
if ((input_update_queued_ || recompile_queued_) && !AllProcessorsAreAvailable()) {
qDebug() << "Blocking render while we wait for all workers to finish...";
return;
}
NodeDependency dep = NodeDependency(GetDependentInput()->get_connected_node(), range.in(), range.out());
if (input_update_queued_) {
UpdateNodeInputs();
input_update_queued_ = false;
}
if (recompile_queued_) {
Decompile();
recompile_queued_ = false;
}
if (!compiled_ && !Compile()) {
qDebug() << "Graph remains uncompiled, nothing to be done";
return;
}
foreach (RenderWorker* worker, processors_) {
if (worker->IsAvailable() || worker == processors_.last()) {
if (cache_queue_.isEmpty()) {
break;
}
if (!WorkerIsBusy(worker)) {
TimeRange cache_frame = cache_queue_.takeFirst();
NodeDependency dep = NodeDependency(GetDependentInput()->get_connected_node(), cache_frame.in(), cache_frame.out());
QMetaObject::invokeMethod(worker,
"Render",
Qt::QueuedConnection,
Q_ARG(NodeDependency, dep));
return true;
SetWorkerBusyState(worker, true);
}
}
return false;
}
ViewerOutput *RenderBackend::viewer_node() const
@@ -244,11 +265,6 @@ bool RenderBackend::ViewerIsConnected() const
return viewer_node_ != nullptr;
}
DecoderCache *RenderBackend::decoder_cache()
{
return &decoder_cache_;
}
const QString &RenderBackend::cache_id() const
{
return cache_id_;
@@ -274,6 +290,27 @@ void RenderBackend::UpdateNodeInputs()
}
}
bool RenderBackend::WorkerIsBusy(RenderWorker *worker) const
{
return processor_busy_state_.at(processors_.indexOf(worker));
}
void RenderBackend::SetWorkerBusyState(RenderWorker *worker, bool busy)
{
processor_busy_state_.replace(processors_.indexOf(worker), busy);
}
bool RenderBackend::AllProcessorsAreAvailable() const
{
foreach (bool busy, processor_busy_state_) {
if (busy) {
return false;
}
}
return true;
}
const QVector<QThread *> &RenderBackend::threads()
{
return threads_;
@@ -291,7 +328,6 @@ void RenderBackend::InitWorkers()
QThread* thread = threads().at(i);
// Connect to it
connect(processor, SIGNAL(RequestSibling(NodeDependency)), this, SLOT(ThreadRequestedSibling(NodeDependency)));
ConnectWorkerToThis(processor);
// Finally, we can move it to its own thread
@@ -300,20 +336,9 @@ void RenderBackend::InitWorkers()
// This function blocks the main thread intentionally. See the documentation for this function to see why.
processor->Init();
}
}
void RenderBackend::ThreadRequestedSibling(NodeDependency dep)
{
// Try to queue another thread to run this dep in advance
foreach (RenderWorker* worker, processors_) {
if (worker->IsAvailable()) {
QMetaObject::invokeMethod(worker,
"RenderAsSibling",
Qt::QueuedConnection,
Q_ARG(NodeDependency, dep));
return;
}
}
processor_busy_state_.resize(processors_.size());
processor_busy_state_.fill(false);
}
void RenderBackend::QueueRecompile()
+9 -11
View File
@@ -30,7 +30,7 @@ public:
bool IsInitiated();
public slots:
virtual void InvalidateCache(const rational &start_range, const rational &end_range) = 0;
virtual void InvalidateCache(const rational &start_range, const rational &end_range);
bool Compile();
@@ -70,8 +70,6 @@ protected:
*/
void CacheNext();
bool GenerateData(const TimeRange& range);
void InitWorkers();
virtual NodeInput* GetDependentInput() = 0;
@@ -82,23 +80,24 @@ protected:
bool ViewerIsConnected() const;
DecoderCache* decoder_cache();
const QString& cache_id() const;
void QueueValueUpdate(const TimeRange& range);
void UpdateNodeInputs();
bool WorkerIsBusy(RenderWorker* worker) const;
void SetWorkerBusyState(RenderWorker* worker, bool busy);
QList<TimeRange> cache_queue_;
QVector<RenderWorker*> processors_;
bool compiled_;
bool caching_;
private:
bool AllProcessorsAreAvailable() const;
/**
* @brief Internal list of RenderProcessThreads
*/
@@ -124,8 +123,6 @@ private:
*/
QString error_;
DecoderCache decoder_cache_;
QString cache_name_;
qint64 cache_time_;
QString cache_id_;
@@ -137,10 +134,11 @@ private:
TimeRange value_update_range_;
bool recompile_queued_;
bool input_update_queued_;
QVector<bool> processor_busy_state_;
private slots:
void ThreadRequestedSibling(NodeDependency dep);
void QueueRecompile();
};
+64
View File
@@ -0,0 +1,64 @@
#ifndef RENDERCACHE_H
#define RENDERCACHE_H
#include <QMap>
#include <QMutex>
template<class K, class V>
class RenderCache
{
public:
RenderCache() = default;
void Clear(){values_.clear();}
void Add(K key, V val){values_.insert(key, val);}
V Get(K key) const {return values_.value(key);}
bool Has(K key) const {return values_.contains(key);}
private:
QMap<K, V> values_;
};
template<class K, class V>
class ThreadSafeRenderCache
{
public:
ThreadSafeRenderCache() = default;
void Clear() {
lock_.lock();
values_.clear();
lock_.unlock();
}
void Add(K key, V val) {
lock_.lock();
values_.insert(key, val);
lock_.unlock();
}
V Get(K key) {
lock_.lock();
V val = values_.value(key);
lock_.unlock();
return val;
}
bool Has(K key) {
lock_.lock();
bool has = values_.contains(key);
lock_.unlock();
return has;
}
private:
QMap<K, V> values_;
QMutex lock_;
};
#endif // RENDERCACHE_H
+17 -30
View File
@@ -4,19 +4,12 @@
#include "node/block/block.h"
RenderWorker::RenderWorker(DecoderCache *decoder_cache, QObject *parent) :
RenderWorker::RenderWorker(QObject *parent) :
QObject(parent),
working_(0),
started_(false),
decoder_cache_(decoder_cache)
started_(false)
{
}
bool RenderWorker::IsAvailable()
{
return (working_ == 0);
}
bool RenderWorker::Init()
{
if (started_) {
@@ -34,6 +27,8 @@ void RenderWorker::Close()
{
CloseInternal();
decoder_cache_.Clear();
started_ = false;
}
@@ -50,9 +45,6 @@ NodeValueTable RenderWorker::RenderAsSibling(NodeDependency dep)
//qDebug() << "Processing" << node->id();
// Set working state
working_++;
// 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()) {
@@ -64,17 +56,9 @@ NodeValueTable RenderWorker::RenderAsSibling(NodeDependency dep)
// We're done!
// End this working state
working_--;
return value;
}
DecoderCache *RenderWorker::decoder_cache()
{
return decoder_cache_;
}
NodeValueTable RenderWorker::RenderInternal(const NodeDependency &path)
{
return RenderAsSibling(path);
@@ -98,17 +82,16 @@ StreamPtr RenderWorker::ResolveStreamFromInput(NodeInput *input)
return input->get_value_at_time(0).value<StreamPtr>();
}
DecoderPtr RenderWorker::ResolveDecoderFromInput(NodeInput *input)
DecoderPtr RenderWorker::ResolveDecoderFromInput(StreamPtr stream)
{
// Access a map of Node inputs and decoder instances and retrieve a frame!
StreamPtr stream = ResolveStreamFromInput(input);
DecoderPtr decoder = decoder_cache()->GetDecoder(stream.get());
DecoderPtr decoder = decoder_cache_.Get(stream.get());
if (decoder == nullptr && stream != nullptr) {
// Create a new Decoder here
DecoderPtr decoder = Decoder::CreateFromID(stream->footage()->decoder());
decoder = Decoder::CreateFromID(stream->footage()->decoder());
decoder->set_stream(stream);
decoder_cache()->AddDecoder(stream.get(), decoder);
decoder_cache_.Add(stream.get(), decoder);
}
return decoder;
@@ -146,13 +129,17 @@ NodeValueTable RenderWorker::ProcessNodeNormally(const NodeDependency& dep)
// Exception for Footage types where we actually retrieve some Footage data from a decoder
if (input->data_type() == NodeParam::kFootage) {
DecoderPtr decoder = ResolveDecoderFromInput(input);
StreamPtr stream = ResolveStreamFromInput(input);
if (decoder) {
FramePtr frame = RetrieveFromDecoder(decoder, input_time);
if (stream) {
DecoderPtr decoder = ResolveDecoderFromInput(stream);
if (frame) {
FrameToValue(frame, &table);
if (decoder) {
FramePtr frame = RetrieveFromDecoder(decoder, input_time);
if (frame) {
FrameToValue(stream, frame, &table);
}
}
}
}
+4 -12
View File
@@ -12,7 +12,7 @@ class RenderWorker : public QObject
{
Q_OBJECT
public:
RenderWorker(DecoderCache* decoder_cache, QObject* parent = nullptr);
RenderWorker(QObject* parent = nullptr);
DISABLE_COPY_MOVE(RenderWorker)
@@ -20,8 +20,6 @@ public:
bool IsStarted();
bool IsAvailable();
public slots:
void Close();
@@ -30,8 +28,6 @@ public slots:
NodeValueTable RenderAsSibling(NodeDependency dep);
signals:
void RequestSibling(NodeDependency path);
void CompletedCache(NodeDependency dep, NodeValueTable data);
protected:
@@ -46,24 +42,20 @@ protected:
virtual void RunNodeAccelerated(Node *node, const NodeValueDatabase *input_params, NodeValueTable* output_params);
StreamPtr ResolveStreamFromInput(NodeInput* input);
DecoderPtr ResolveDecoderFromInput(NodeInput* input);
DecoderPtr ResolveDecoderFromInput(StreamPtr stream);
virtual FramePtr RetrieveFromDecoder(DecoderPtr decoder, const TimeRange& range) = 0;
virtual void FrameToValue(FramePtr frame, NodeValueTable* table) = 0;
virtual void FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable* table) = 0;
NodeValueTable ProcessNodeNormally(const NodeDependency &dep);
virtual NodeValueTable RenderBlock(TrackOutput *track, const TimeRange& range) = 0;
DecoderCache* decoder_cache();
QAtomicInt working_;
private:
bool started_;
DecoderCache* decoder_cache_;
DecoderCache decoder_cache_;
};
+3 -1
View File
@@ -43,6 +43,8 @@ void VideoRenderBackend::InvalidateCache(const rational &start_range, const rati
return;
}
RenderBackend::InvalidateCache(start_range, end_range);
// Adjust range to min/max values
rational start_range_adj = qMax(rational(0), start_range);
rational end_range_adj = qMin(SequenceLength(), end_range);
@@ -175,7 +177,7 @@ void VideoRenderBackend::CacheIDChangedEvent(const QString &id)
void VideoRenderBackend::ConnectWorkerToThis(RenderWorker *processor)
{
connect(processor, SIGNAL(CompletedFrame(NodeDependency, QByteArray, NodeValueTable)), this, SLOT(ThreadCompletedFrame(NodeDependency, QByteArray, NodeValueTable)));
connect(processor, SIGNAL(HashAlreadyBeingCached()), this, SLOT(ThreadSkippedFrame()));
connect(processor, SIGNAL(HashAlreadyBeingCached(NodeDependency, QByteArray)), this, SLOT(ThreadSkippedFrame(NodeDependency, QByteArray)));
connect(processor, SIGNAL(CompletedDownload(NodeDependency, QByteArray)), this, SLOT(ThreadCompletedDownload(NodeDependency, QByteArray)));
connect(processor, SIGNAL(HashAlreadyExists(NodeDependency, QByteArray)), this, SLOT(ThreadHashAlreadyExists(NodeDependency, QByteArray)));
}
+1
View File
@@ -23,6 +23,7 @@
#include <QLinkedList>
#include "colorprocessorcache.h"
#include "node/output/viewer/viewer.h"
#include "renderbackend.h"
#include "render/pixelformat.h"
+17 -2
View File
@@ -46,7 +46,7 @@ void VideoRenderFrameCache::SetCacheID(const QString &id)
cache_id_ = id;
}
QByteArray VideoRenderFrameCache::TimeToHash(const rational &time)
QByteArray VideoRenderFrameCache::TimeToHash(const rational &time) const
{
return time_hash_map_.value(time);
}
@@ -80,6 +80,21 @@ void VideoRenderFrameCache::Truncate(const rational &time)
}
}
QList<rational> VideoRenderFrameCache::TimesWithHash(const QByteArray &hash)
{
QList<rational> list;
QMap<rational, QByteArray>::const_iterator iterator;
for (iterator=time_hash_map_.begin();iterator!=time_hash_map_.end();iterator++) {
if (iterator.value() == hash) {
list.append(iterator.key());
}
}
return list;
}
void VideoRenderFrameCache::RemoveHashFromCurrentlyCaching(const QByteArray &hash)
{
currently_caching_lock_.lock();
@@ -87,7 +102,7 @@ void VideoRenderFrameCache::RemoveHashFromCurrentlyCaching(const QByteArray &has
currently_caching_lock_.unlock();
}
QString VideoRenderFrameCache::CachePathName(const QByteArray &hash)
QString VideoRenderFrameCache::CachePathName(const QByteArray &hash) const
{
QDir this_cache_dir = QDir(GetMediaCacheLocation()).filePath(cache_id_);
this_cache_dir.mkpath(".");
+4 -2
View File
@@ -28,17 +28,19 @@ public:
/**
* @brief Return the path of the cached image at this time
*/
QString CachePathName(const QByteArray &hash);
QString CachePathName(const QByteArray &hash) const;
void SetCacheID(const QString& id);
QByteArray TimeToHash(const rational& time);
QByteArray TimeToHash(const rational& time) const;
void SetHash(const rational& time, const QByteArray& hash);
void RemoveHash(const rational& time, const QByteArray &hash);
void Truncate(const rational& time);
QList<rational> TimesWithHash(const QByteArray& hash);
private:
void RemoveHashFromCurrentlyCaching(const QByteArray& hash);
+21 -11
View File
@@ -4,8 +4,8 @@
#include "node/node.h"
#include "render/pixelservice.h"
VideoRenderWorker::VideoRenderWorker(DecoderCache *decoder_cache, VideoRenderFrameCache *frame_cache, QObject *parent) :
RenderWorker(decoder_cache, parent),
VideoRenderWorker::VideoRenderWorker(VideoRenderFrameCache *frame_cache, QObject *parent) :
RenderWorker(parent),
frame_cache_(frame_cache)
{
@@ -36,7 +36,7 @@ NodeValueTable VideoRenderWorker::RenderInternal(const NodeDependency& path)
emit CompletedFrame(path, hash, value);
} else {
// Another thread must be caching this already, nothing to be done
emit HashAlreadyBeingCached();
emit HashAlreadyBeingCached(path, hash);
}
return value;
@@ -85,7 +85,7 @@ void VideoRenderWorker::HashNodeRecursively(QCryptographicHash *hash, Node* n, c
// 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(input);
DecoderPtr decoder = ResolveDecoderFromInput(stream);
if (decoder != nullptr) {
// Add footage details to hash
@@ -99,10 +99,19 @@ void VideoRenderWorker::HashNodeRecursively(QCryptographicHash *hash, Node* n, c
// Footage stream
hash->addData(QString::number(stream->index()).toUtf8());
// Footage timestamp
hash->addData(QString::number(decoder->GetTimestampFromTime(time)).toUtf8());
if (stream->type() == Stream::kImage || stream->type() == Stream::kVideo) {
ImageStreamPtr video_stream = std::static_pointer_cast<ImageStream>(stream);
// FIXME: Add colorspace and alpha assoc
// Footage timestamp
hash->addData(QString::number(decoder->GetTimestampFromTime(time)).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());
}
}
}
}
@@ -130,8 +139,6 @@ void VideoRenderWorker::CloseInternal()
void VideoRenderWorker::Download(NodeDependency dep, QByteArray hash, QVariant texture, QString filename)
{
working_++;
PixelFormatInfo format_info = PixelService::GetPixelFormatInfo(video_params().format());
// Set up OIIO::ImageSpec for compressing cached images on disk
@@ -153,8 +160,6 @@ void VideoRenderWorker::Download(NodeDependency dep, QByteArray hash, QVariant t
} else {
qWarning() << "Failed to open output file:" << filename;
}
working_--;
}
NodeValueTable VideoRenderWorker::RenderBlock(TrackOutput *track, const TimeRange &range)
@@ -171,3 +176,8 @@ NodeValueTable VideoRenderWorker::RenderBlock(TrackOutput *track, const TimeRang
return table;
}
ColorProcessorCache *VideoRenderWorker::color_cache()
{
return &color_cache_;
}
+7 -2
View File
@@ -3,6 +3,7 @@
#include <QCryptographicHash>
#include "colorprocessorcache.h"
#include "node/dependency.h"
#include "render/videoparams.h"
#include "renderworker.h"
@@ -11,7 +12,7 @@
class VideoRenderWorker : public RenderWorker {
Q_OBJECT
public:
VideoRenderWorker(DecoderCache* decoder_cache, VideoRenderFrameCache* frame_cache, QObject* parent = nullptr);
VideoRenderWorker(VideoRenderFrameCache* frame_cache, QObject* parent = nullptr);
void SetParameters(const VideoRenderingParams& video_params);
@@ -23,7 +24,7 @@ signals:
void CompletedDownload(NodeDependency path, QByteArray hash);
void HashAlreadyBeingCached();
void HashAlreadyBeingCached(NodeDependency path, QByteArray hash);
void HashAlreadyExists(NodeDependency path, QByteArray hash);
@@ -44,6 +45,8 @@ protected:
virtual NodeValueTable RenderBlock(TrackOutput *track, const TimeRange& range) override;
ColorProcessorCache* color_cache();
private:
void ProcessNode();
@@ -53,6 +56,8 @@ private:
VideoRenderFrameCache* frame_cache_;
ColorProcessorCache color_cache_;
QByteArray download_buffer_;
private slots:
+4
View File
@@ -4,8 +4,10 @@
#include <OpenColorIO/OpenColorIO.h>
namespace OCIO = OCIO_NAMESPACE::v1;
#include "common/constructors.h"
#include "decoder/frame.h"
class ColorProcessor;
using ColorProcessorPtr = std::shared_ptr<ColorProcessor>;
@@ -19,6 +21,8 @@ public:
QString view,
const QString& look);
DISABLE_COPY_MOVE(ColorProcessor)
static ColorProcessorPtr Create(const QString& source_space, const QString& dest_space);
static ColorProcessorPtr Create(const QString& source_space,
+1 -1
View File
@@ -239,7 +239,7 @@ void TimelineWidget::PointerTool::InitiateDrag(const TimelineCoordinate &mouse_p
olive::timeline::MovementMode trim_mode = olive::timeline::kNone;
// FIXME: Hardcoded number
const int kTrimHandle = 20;
const int kTrimHandle = 10;
qreal mouse_x = parent()->TimeToScene(mouse_pos.GetFrame());