Merge branch 'master' into cache-update

This commit is contained in:
itsmattkc
2022-07-01 22:32:49 -05:00
98 changed files with 1638 additions and 1480 deletions
+2 -2
View File
@@ -48,8 +48,6 @@ set(OLIVE_SOURCES
render/renderer.cpp
render/renderer.h
render/rendercache.h
render/rendererthreadwrapper.cpp
render/rendererthreadwrapper.h
render/renderjobtracker.cpp
render/renderjobtracker.h
render/rendermanager.cpp
@@ -57,6 +55,8 @@ set(OLIVE_SOURCES
render/rendermodes.h
render/renderprocessor.cpp
render/renderprocessor.h
render/renderticket.cpp
render/renderticket.h
render/shadercode.h
render/subtitleparams.cpp
render/subtitleparams.h
+10 -13
View File
@@ -61,7 +61,7 @@ PreviewAutoCacher::~PreviewAutoCacher()
SetViewerNode(nullptr);
}
RenderTicketPtr PreviewAutoCacher::GetSingleFrame(const rational &t, RenderTicketPriority priority)
RenderTicketPtr PreviewAutoCacher::GetSingleFrame(const rational &t)
{
// If we have a single frame render queued (but not yet sent to the RenderManager), cancel it now
CancelQueuedSingleFrameRender();
@@ -70,7 +70,6 @@ RenderTicketPtr PreviewAutoCacher::GetSingleFrame(const rational &t, RenderTicke
auto sfr = std::make_shared<RenderTicket>();
sfr->Start();
sfr->setProperty("time", QVariant::fromValue(t));
sfr->setProperty("priority", int(priority));
// Queue it and try to render
single_frame_render_ = sfr;
@@ -79,9 +78,9 @@ RenderTicketPtr PreviewAutoCacher::GetSingleFrame(const rational &t, RenderTicke
return sfr;
}
RenderTicketPtr PreviewAutoCacher::GetRangeOfAudio(TimeRange range, RenderTicketPriority priority)
RenderTicketPtr PreviewAutoCacher::GetRangeOfAudio(TimeRange range)
{
return RenderAudio(copied_viewer_node_->GetConnectedSampleOutput(), range, priority, nullptr);
return RenderAudio(copied_viewer_node_->GetConnectedSampleOutput(), range, nullptr);
}
void PreviewAutoCacher::ClearSingleFrameRenders()
@@ -600,7 +599,6 @@ void PreviewAutoCacher::TryRender()
// Check if already caching this
RenderTicketWatcher *watcher = RenderFrame(copied_viewer_node_->GetConnectedTextureOutput(),
single_frame_render_->property("time").value<rational>(),
RenderTicketPriority(single_frame_render_->property("priority").toInt()),
nullptr);
video_immediate_passthroughs_[watcher].append(single_frame_render_);
@@ -608,8 +606,8 @@ void PreviewAutoCacher::TryRender()
}
if (!pause_renders_) {
// Ensure we are running tasks if we have any
const int max_tasks = RenderManager::GetNumberOfIdealConcurrentJobs();
// Completely arbitrary number. I don't know what's optimal for this yet.
const int max_tasks = 4;
// Handle video tasks
while (!pending_video_jobs_.empty()) {
@@ -619,7 +617,7 @@ void PreviewAutoCacher::TryRender()
// Queue next frames
rational t;
while (running_video_tasks_.size() < max_tasks && d.iterator.GetNext(&t)) {
RenderFrame(copy, t, RenderTicketPriority::kNormal, d.cache);
RenderFrame(copy, t, d.cache);
emit SignalCacheProxyTaskProgress(double(d.iterator.frame_index()) / double(d.iterator.size()));
@@ -644,7 +642,7 @@ void PreviewAutoCacher::TryRender()
// Start job
if (Node *copy = copy_map_.value(d.node)) {
RenderAudio(copy, d.range, RenderTicketPriority::kNormal, d.cache);
RenderAudio(copy, d.range, d.cache);
} else {
qCritical() << "Failed to find node copy for audio job";
}
@@ -654,13 +652,14 @@ void PreviewAutoCacher::TryRender()
}
}
RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational& time, RenderTicketPriority priority, PlaybackCache *cache)
RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational& time, PlaybackCache *cache)
{
RenderTicketWatcher* watcher = new RenderTicketWatcher();
watcher->setProperty("job", QVariant::fromValue(last_update_time_));
watcher->setProperty("cache", Node::PtrToValue(cache));
watcher->setProperty("time", QVariant::fromValue(time));
connect(watcher, &RenderTicketWatcher::Finished, this, &PreviewAutoCacher::VideoRendered);
running_video_tasks_.append(watcher);
RenderManager::RenderVideoParams rvp(node,
@@ -683,7 +682,6 @@ RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational&
rvp.AddCache(frame_cache);
}
rvp.priority = priority;
rvp.return_type = RenderManager::kTexture;
rvp.use_cache = true;
@@ -692,7 +690,7 @@ RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational&
return watcher;
}
RenderTicketPtr PreviewAutoCacher::RenderAudio(Node *node, const TimeRange &r, RenderTicketPriority priority, PlaybackCache *cache)
RenderTicketPtr PreviewAutoCacher::RenderAudio(Node *node, const TimeRange &r, PlaybackCache *cache)
{
RenderTicketWatcher* watcher = new RenderTicketWatcher();
watcher->setProperty("job", QVariant::fromValue(last_update_time_));
@@ -707,7 +705,6 @@ RenderTicketPtr PreviewAutoCacher::RenderAudio(Node *node, const TimeRange &r, R
copied_viewer_node_->GetAudioParams());
rap.generate_waveforms = dynamic_cast<AudioWaveformCache*>(cache);
rap.priority = priority;
rap.clamp = false;
RenderTicketPtr ticket = RenderManager::instance()->RenderAudio(rap);
+4 -6
View File
@@ -33,8 +33,6 @@
#include "render/audioparams.h"
#include "render/renderjobtracker.h"
#include "render/rendermanager.h"
#include "threading/threadpool.h"
#include "threading/threadticketwatcher.h"
namespace olive {
@@ -51,9 +49,9 @@ public:
virtual ~PreviewAutoCacher() override;
RenderTicketPtr GetSingleFrame(const rational& t, RenderTicketPriority prioritize);
RenderTicketPtr GetSingleFrame(const rational& t);
RenderTicketPtr GetRangeOfAudio(TimeRange range, RenderTicketPriority prioritize);
RenderTicketPtr GetRangeOfAudio(TimeRange range);
void ClearSingleFrameRenders();
@@ -105,9 +103,9 @@ signals:
private:
void TryRender();
RenderTicketWatcher *RenderFrame(Node *node, const rational &time, RenderTicketPriority priority, PlaybackCache *cache);
RenderTicketWatcher *RenderFrame(Node *node, const rational &time, PlaybackCache *cache);
RenderTicketPtr RenderAudio(Node *node, const TimeRange &range, RenderTicketPriority priority, PlaybackCache *cache);
RenderTicketPtr RenderAudio(Node *node, const TimeRange &range, PlaybackCache *cache);
/**
* @brief Process all changes to internal NodeGraph copy
-181
View File
@@ -1,181 +0,0 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2022 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 "rendererthreadwrapper.h"
namespace olive {
RendererThreadWrapper::RendererThreadWrapper(Renderer *inner, QObject *parent) :
Renderer(parent),
inner_(inner),
thread_(nullptr)
{
}
bool RendererThreadWrapper::Init()
{
// Init context in main thread
if (!inner_->Init()) {
return false;
}
// Create thread
thread_ = new QThread(this);
thread_->start(QThread::IdlePriority);
// Move context to thread
inner_->moveToThread(thread_);
// Queue post-init in new thread
QMetaObject::invokeMethod(inner_, "PostInit", Qt::BlockingQueuedConnection);
return true;
}
void RendererThreadWrapper::PostInit()
{
// Do nothing
}
void RendererThreadWrapper::DestroyInternal()
{
if (thread_) {
QMetaObject::invokeMethod(inner_, "DestroyInternal", Qt::BlockingQueuedConnection);
thread_->quit();
thread_->wait();
delete thread_;
thread_ = nullptr;
// Destroy in main thread
inner_->PostDestroy();
}
}
void RendererThreadWrapper::ClearDestination(Texture *texture, double r, double g, double b, double a)
{
QMetaObject::invokeMethod(inner_, "ClearDestination", Qt::BlockingQueuedConnection,
OLIVE_NS_ARG(Texture*, texture),
Q_ARG(double, r),
Q_ARG(double, g),
Q_ARG(double, b),
Q_ARG(double, a));
}
QVariant RendererThreadWrapper::CreateNativeTexture2D(int width, int height, VideoParams::Format format, int channel_count, const void *data, int linesize)
{
QVariant v;
QMetaObject::invokeMethod(inner_, "CreateNativeTexture2D", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QVariant, v),
Q_ARG(int, width),
Q_ARG(int, height),
OLIVE_NS_ARG(VideoParams::Format, format),
Q_ARG(int, channel_count),
Q_ARG(const void*, data),
Q_ARG(int, linesize));
return v;
}
QVariant RendererThreadWrapper::CreateNativeTexture3D(int width, int height, int depth, VideoParams::Format format, int channel_count, const void *data, int linesize)
{
QVariant v;
QMetaObject::invokeMethod(inner_, "CreateNativeTexture3D", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QVariant, v),
Q_ARG(int, width),
Q_ARG(int, height),
Q_ARG(int, depth),
OLIVE_NS_ARG(VideoParams::Format, format),
Q_ARG(int, channel_count),
Q_ARG(const void*, data),
Q_ARG(int, linesize));
return v;
}
void RendererThreadWrapper::DestroyNativeTexture(QVariant texture)
{
QMetaObject::invokeMethod(inner_, "DestroyNativeTexture", Qt::BlockingQueuedConnection,
Q_ARG(QVariant, texture));
}
QVariant RendererThreadWrapper::CreateNativeShader(ShaderCode code)
{
QVariant v;
QMetaObject::invokeMethod(inner_, "CreateNativeShader", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QVariant, v),
OLIVE_NS_ARG(ShaderCode, code));
return v;
}
void RendererThreadWrapper::DestroyNativeShader(QVariant shader)
{
QMetaObject::invokeMethod(inner_, "DestroyNativeShader", Qt::BlockingQueuedConnection,
Q_ARG(QVariant, shader));
}
void RendererThreadWrapper::UploadToTexture(Texture *texture, const void *data, int linesize)
{
QMetaObject::invokeMethod(inner_, "UploadToTexture", Qt::BlockingQueuedConnection,
OLIVE_NS_ARG(Texture*, texture),
Q_ARG(const void*, data),
Q_ARG(int, linesize));
}
void RendererThreadWrapper::DownloadFromTexture(Texture *texture, void *data, int linesize)
{
QMetaObject::invokeMethod(inner_, "DownloadFromTexture", Qt::BlockingQueuedConnection,
OLIVE_NS_ARG(Texture*, texture),
Q_ARG(void*, data),
Q_ARG(int, linesize));
}
void RendererThreadWrapper::Flush()
{
QMetaObject::invokeMethod(inner_, "Flush", Qt::BlockingQueuedConnection);
}
Color RendererThreadWrapper::GetPixelFromTexture(Texture *texture, const QPointF &pt)
{
Color c;
QMetaObject::invokeMethod(inner_, "GetPixelFromTexture", Qt::BlockingQueuedConnection,
OLIVE_NS_RETURN_ARG(Color, c),
OLIVE_NS_ARG(Texture*, texture),
Q_ARG(QPointF, pt));
return c;
}
void RendererThreadWrapper::Blit(QVariant shader, ShaderJob job, Texture *destination, VideoParams destination_params, bool clear_destination)
{
QMetaObject::invokeMethod(inner_, "Blit", Qt::BlockingQueuedConnection,
Q_ARG(QVariant, shader),
OLIVE_NS_ARG(ShaderJob, job),
OLIVE_NS_ARG(Texture*, destination),
OLIVE_NS_ARG(VideoParams, destination_params),
Q_ARG(bool, clear_destination));
}
}
-86
View File
@@ -1,86 +0,0 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2022 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 RENDERCONTEXTTHREADWRAPPER_H
#define RENDERCONTEXTTHREADWRAPPER_H
#include <QThread>
#include "renderer.h"
namespace olive {
class RendererThreadWrapper : public Renderer
{
public:
RendererThreadWrapper(Renderer* inner, QObject* parent = nullptr);
virtual ~RendererThreadWrapper() override
{
Destroy();
PostDestroy();
delete inner_;
}
virtual bool Init() override;
virtual void PostDestroy() override {}
public slots:
virtual void PostInit() override;
virtual void DestroyInternal() override;
virtual void ClearDestination(olive::Texture *texture = nullptr, double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) override;
virtual QVariant CreateNativeTexture2D(int width, int height, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) override;
virtual QVariant CreateNativeTexture3D(int width, int height, int depth, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) override;
virtual void DestroyNativeTexture(QVariant texture) override;
virtual QVariant CreateNativeShader(olive::ShaderCode code) override;
virtual void DestroyNativeShader(QVariant shader) override;
virtual void UploadToTexture(olive::Texture* texture, const void* data, int linesize) override;
virtual void DownloadFromTexture(olive::Texture* texture, void* data, int linesize) override;
virtual void Flush() override;
virtual Color GetPixelFromTexture(olive::Texture *texture, const QPointF &pt) override;
protected slots:
virtual void Blit(QVariant shader,
olive::ShaderJob job,
olive::Texture* destination,
olive::VideoParams destination_params,
bool clear_destination) override;
private:
Renderer* inner_;
QThread* thread_;
};
}
#endif // RENDERCONTEXTTHREADWRAPPER_H
+140 -23
View File
@@ -27,7 +27,6 @@
#include "config/config.h"
#include "core.h"
#include "render/opengl/openglrenderer.h"
#include "render/rendererthreadwrapper.h"
#include "renderprocessor.h"
#include "task/conform/conform.h"
#include "task/taskmanager.h"
@@ -38,20 +37,11 @@ namespace olive {
RenderManager* RenderManager::instance_ = nullptr;
RenderManager::RenderManager(QObject *parent) :
ThreadPool(0, parent),
backend_(kOpenGL)
backend_(kOpenGL),
aggressive_gc_(0)
{
Renderer* graphics_renderer = nullptr;
if (backend_ == kOpenGL) {
graphics_renderer = new OpenGLRenderer();
}
if (graphics_renderer) {
context_ = new RendererThreadWrapper(graphics_renderer, this);
context_->Init();
context_->PostInit();
context_ = new OpenGLRenderer();
decoder_cache_ = new DecoderCache();
shader_cache_ = new ShaderCache();
} else {
@@ -59,6 +49,19 @@ RenderManager::RenderManager(QObject *parent) :
context_ = nullptr;
decoder_cache_ = nullptr;
}
if (context_) {
video_thread_ = new RenderThread(context_, decoder_cache_, shader_cache_, this);
audio_thread_ = new RenderThread(nullptr, decoder_cache_, shader_cache_, this);
video_thread_->start(QThread::IdlePriority);
audio_thread_->start(QThread::IdlePriority);
}
decoder_clear_timer_ = new QTimer(this);
decoder_clear_timer_->setInterval(kDecoderMaximumInactivity);
connect(decoder_clear_timer_, &QTimer::timeout, this, &RenderManager::ClearOldDecoders);
decoder_clear_timer_->start();
}
RenderManager::~RenderManager()
@@ -67,9 +70,14 @@ RenderManager::~RenderManager()
delete shader_cache_;
delete decoder_cache_;
context_->Destroy();
video_thread_->quit();
video_thread_->wait();
context_->PostDestroy();
delete context_;
audio_thread_->quit();
audio_thread_->wait();
}
}
@@ -95,7 +103,7 @@ RenderTicketPtr RenderManager::RenderFrame(const RenderVideoParams &params)
ticket->setProperty("cachetimebase", QVariant::fromValue(params.cache_timebase));
ticket->setProperty("cacheid", QVariant::fromValue(params.cache_id));
AddTicket(ticket, params.priority);
video_thread_->AddTicket(ticket);
return ticket;
}
@@ -112,22 +120,131 @@ RenderTicketPtr RenderManager::RenderAudio(const RenderAudioParams &params)
ticket->setProperty("clamp", params.clamp);
ticket->setProperty("aparam", QVariant::fromValue(params.audio_params));
AddTicket(ticket, params.priority);
audio_thread_->AddTicket(ticket);
return ticket;
}
void RenderManager::RunTicket(RenderTicketPtr ticket) const
bool RenderManager::RemoveTicket(RenderTicketPtr ticket)
{
// Setup the ticket for ::Process
ticket->Start();
if (video_thread_->RemoveTicket(ticket)) {
return true;
} else if (audio_thread_->RemoveTicket(ticket)) {
return true;
} else {
return false;
}
}
if (ticket->IsCancelled()) {
ticket->Finish();
return;
void RenderManager::SetAggressiveGarbageCollection(bool enabled)
{
aggressive_gc_ += enabled ? 1 : -1;
if (aggressive_gc_ > 0) {
decoder_clear_timer_->setInterval(kDecoderMaximumInactivityAggressive);
} else {
decoder_clear_timer_->setInterval(kDecoderMaximumInactivity);
}
}
void RenderManager::ClearOldDecoders()
{
QMutexLocker locker(decoder_cache_->mutex());
qint64 min_age = QDateTime::currentMSecsSinceEpoch() - kDecoderMaximumInactivity;
for (auto it=decoder_cache_->begin(); it!=decoder_cache_->end(); ) {
DecoderPair decoder = it.value();
if (decoder.decoder->GetLastAccessedTime() < min_age) {
decoder.decoder->Close();
it = decoder_cache_->erase(it);
} else {
it++;
}
}
}
RenderThread::RenderThread(Renderer *renderer, DecoderCache *decoder_cache, ShaderCache *shader_cache, QObject *parent) :
QThread(parent),
cancelled_(false),
context_(renderer),
decoder_cache_(decoder_cache),
shader_cache_(shader_cache)
{
if (context_) {
context_->Init();
context_->moveToThread(this);
}
}
void RenderThread::AddTicket(RenderTicketPtr ticket)
{
QMutexLocker locker(&mutex_);
queue_.push_back(ticket);
wait_.wakeOne();
}
bool RenderThread::RemoveTicket(RenderTicketPtr ticket)
{
QMutexLocker locker(&mutex_);
auto it = std::find(queue_.begin(), queue_.end(), ticket);
if (it == queue_.end()) {
return false;
}
RenderProcessor::Process(ticket, context_, decoder_cache_, shader_cache_);
queue_.erase(it);
return true;
}
void RenderThread::quit()
{
QMutexLocker locker(&mutex_);
cancelled_ = true;
wait_.wakeOne();
}
void RenderThread::run()
{
if (context_) {
context_->PostInit();
}
QMutexLocker locker(&mutex_);
while (!cancelled_) {
if (queue_.empty()) {
wait_.wait(&mutex_);
}
if (cancelled_) {
break;
}
if (!queue_.empty()) {
RenderTicketPtr ticket = queue_.front();
queue_.pop_front();
locker.unlock();
// Setup the ticket for ::Process
ticket->Start();
if (ticket->IsCancelled()) {
ticket->Finish();
} else {
RenderProcessor::Process(ticket, context_, decoder_cache_, shader_cache_);
}
locker.relock();
}
}
if (context_) {
context_->Destroy();
context_->moveToThread(this->thread());
}
}
}
+50 -11
View File
@@ -30,12 +30,44 @@
#include "node/output/viewer/viewer.h"
#include "node/traverser.h"
#include "render/renderer.h"
#include "render/renderticket.h"
#include "rendercache.h"
#include "threading/threadpool.h"
namespace olive {
class RenderManager : public ThreadPool
class RenderThread : public QThread
{
Q_OBJECT
public:
RenderThread(Renderer *renderer, DecoderCache *decoder_cache, ShaderCache *shader_cache, QObject *parent = nullptr);
void AddTicket(RenderTicketPtr ticket);
bool RemoveTicket(RenderTicketPtr ticket);
void quit();
protected:
virtual void run() override;
private:
QMutex mutex_;
QWaitCondition wait_;
std::list<RenderTicketPtr> queue_;
bool cancelled_;
Renderer *context_;
DecoderCache *decoder_cache_;
ShaderCache *shader_cache_;
};
class RenderManager : public QObject
{
Q_OBJECT
public:
@@ -78,7 +110,6 @@ public:
time = t;
color_manager = colorman;
use_cache = false;
priority = RenderTicketPriority::kNormal;
return_type = kFrame;
force_format = VideoParams::kFormatInvalid;
force_color_output = nullptr;
@@ -98,7 +129,6 @@ public:
rational time;
ColorManager *color_manager;
bool use_cache;
RenderTicketPriority priority;
ReturnType return_type;
QString cache_dir;
@@ -128,7 +158,6 @@ public:
range = time;
audio_params = aparam;
generate_waveforms = false;
priority = RenderTicketPriority::kNormal;
clamp = true;
}
@@ -136,7 +165,6 @@ public:
TimeRange range;
AudioParams audio_params;
bool generate_waveforms;
RenderTicketPriority priority;
bool clamp;
};
@@ -149,7 +177,7 @@ public:
*/
RenderTicketPtr RenderAudio(const RenderAudioParams &params);
virtual void RunTicket(RenderTicketPtr ticket) const override;
bool RemoveTicket(RenderTicketPtr ticket);
enum TicketType {
kTypeVideo,
@@ -161,10 +189,8 @@ public:
return backend_;
}
static int GetNumberOfIdealConcurrentJobs()
{
return QThread::idealThreadCount();
}
public slots:
void SetAggressiveGarbageCollection(bool enabled);
signals:
@@ -183,6 +209,19 @@ private:
ShaderCache* shader_cache_;
static constexpr auto kDecoderMaximumInactivityAggressive = 1000;
static constexpr auto kDecoderMaximumInactivity = 5000;
int aggressive_gc_;
QTimer *decoder_clear_timer_;
RenderThread *video_thread_;
RenderThread *audio_thread_;
private slots:
void ClearOldDecoders();
};
}
+28 -1
View File
@@ -388,6 +388,10 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim
void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time)
{
if (!render_ctx_) {
return;
}
if (ticket_->property("type").value<RenderManager::TicketType>() != RenderManager::kTypeVideo) {
// Video cannot contribute to audio, so we do nothing here
return;
@@ -494,7 +498,9 @@ void RenderProcessor::ProcessAudioFootage(SampleBuffer &destination, const Foota
void RenderProcessor::ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob &job)
{
Q_UNUSED(range)
if (!render_ctx_) {
return;
}
QString full_shader_id = QStringLiteral("%1:%2").arg(node->id(), job.GetShaderID());
@@ -549,11 +555,19 @@ void RenderProcessor::ProcessSamples(SampleBuffer &destination, const Node *node
void RenderProcessor::ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob &job)
{
if (!render_ctx_) {
return;
}
render_ctx_->BlitColorManaged(job, destination.get());
}
void RenderProcessor::ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob &job)
{
if (!render_ctx_) {
return;
}
FramePtr frame = Frame::Create();
frame->set_video_params(destination->params());
@@ -582,8 +596,21 @@ TexturePtr RenderProcessor::ProcessVideoCacheJob(const CacheJob &val)
return nullptr;
}
TexturePtr RenderProcessor::CreateTexture(const VideoParams &p)
{
if (render_ctx_) {
return render_ctx_->CreateTexture(p);
} else {
return super::CreateTexture(p);
}
}
void RenderProcessor::ConvertToReferenceSpace(TexturePtr destination, TexturePtr source, const QString &input_cs)
{
if (!render_ctx_) {
return;
}
ColorManager* color_manager = Node::ValueToPtr<ColorManager>(ticket_->property("colormanager"));
ColorProcessorPtr cp = ColorProcessor::Create(color_manager, input_cs, color_manager->GetReferenceColorSpace());
+2 -5
View File
@@ -25,7 +25,7 @@
#include "node/traverser.h"
#include "render/renderer.h"
#include "rendercache.h"
#include "threading/threadticket.h"
#include "renderticket.h"
namespace olive {
@@ -58,10 +58,7 @@ protected:
virtual TexturePtr ProcessVideoCacheJob(const CacheJob &val) override;
virtual TexturePtr CreateTexture(const VideoParams &p) override
{
return render_ctx_->CreateTexture(p);
}
virtual TexturePtr CreateTexture(const VideoParams &p) override;
virtual SampleBuffer CreateSampleBuffer(const AudioParams &params, int sample_count) override
{
+209
View File
@@ -0,0 +1,209 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2022 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 "renderticket.h"
namespace olive {
RenderTicket::RenderTicket() :
is_running_(false),
has_result_(false),
finish_count_(0)
{
}
void RenderTicket::WaitForFinished(QMutex *mutex)
{
if (is_running_) {
wait_.wait(mutex);
}
}
void RenderTicket::Start()
{
QMutexLocker locker(&lock_);
is_running_ = true;
has_result_ = false;
result_.clear();
}
void RenderTicket::Finish()
{
FinishInternal(false, QVariant());
}
void RenderTicket::Finish(QVariant result)
{
FinishInternal(true, result);
}
QVariant RenderTicket::Get()
{
WaitForFinished();
// We don't have to mutex around this because there is no way to write to `result_` after
// the ticket has finished and the above function blocks the calling thread until it is finished
return result_;
}
void RenderTicket::WaitForFinished()
{
QMutexLocker locker(&lock_);
WaitForFinished(&lock_);
}
bool RenderTicket::IsRunning(bool lock)
{
if (lock) {
lock_.lock();
}
bool running = is_running_;
if (lock) {
lock_.unlock();
}
return running;
}
int RenderTicket::GetFinishCount(bool lock)
{
if (lock) {
lock_.lock();
}
int count = finish_count_;
if (lock) {
lock_.unlock();
}
return count;
}
bool RenderTicket::HasResult()
{
QMutexLocker locker(&lock_);
return has_result_;
}
void RenderTicket::FinishInternal(bool has_result, QVariant result)
{
QMutexLocker locker(&lock_);
if (!is_running_) {
qWarning() << "Tried to finish ticket that wasn't running";
} else {
is_running_ = false;
has_result_ = has_result;
result_ = result;
finish_count_++;
wait_.wakeAll();
locker.unlock();
emit Finished();
}
}
RenderTicketWatcher::RenderTicketWatcher(QObject *parent) :
QObject(parent),
ticket_(nullptr)
{
}
void RenderTicketWatcher::SetTicket(RenderTicketPtr ticket)
{
if (ticket_) {
qCritical() << "Tried to set a ticket on a RenderTicketWatcher twice";
return;
}
if (!ticket) {
qCritical() << "Tried to set a null ticket on a RenderTicketWatcher";
return;
}
ticket_ = ticket;
// Lock ticket so we can query if it's already finished by the time this code runs
QMutexLocker locker(ticket->lock());
connect(ticket_.get(), &RenderTicket::Finished, this, &RenderTicketWatcher::TicketFinished);
if (!ticket_->IsRunning(false) && ticket_->GetFinishCount(false) > 0) {
// Ticket has already finished before, so we emit a signal
locker.unlock();
TicketFinished();
}
}
bool RenderTicketWatcher::IsRunning()
{
if (ticket_) {
return ticket_->IsRunning();
} else {
return false;
}
}
void RenderTicketWatcher::WaitForFinished()
{
if (ticket_) {
ticket_->WaitForFinished();
}
}
QVariant RenderTicketWatcher::Get()
{
if (ticket_) {
return ticket_->Get();
} else {
return QVariant();
}
}
bool RenderTicketWatcher::HasResult()
{
if (ticket_) {
return ticket_->HasResult();
} else {
return false;
}
}
void RenderTicketWatcher::Cancel()
{
if (ticket_) {
ticket_->Cancel();
}
}
void RenderTicketWatcher::TicketFinished()
{
emit Finished(this);
}
}
+173
View File
@@ -0,0 +1,173 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2022 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 RENDERTICKET_H
#define RENDERTICKET_H
#include <QDateTime>
#include <QMutex>
#include <QWaitCondition>
#include "codec/frame.h"
#include "codec/samplebuffer.h"
#include "common/cancelableobject.h"
#include "common/timerange.h"
#include "node/output/viewer/viewer.h"
namespace olive {
class RenderTicket : public QObject, public CancelableObject
{
Q_OBJECT
public:
RenderTicket();
/**
* @brief Get the ticket's current state
*
* This function is thread safe, unless `lock` is set to false. Then the caller has responsibility
* of locking the mutex before and unlocking after this function is called.
*/
bool IsRunning(bool lock = true);
/**
* @brief Determine how many times ticket has been finished
*
* This function is thread safe, unless `lock` is set to false. Then the caller has responsibility
* of locking the mutex before and unlocking after this function is called.
*/
int GetFinishCount(bool lock = true);
/**
* @brief Check if this ticket has a result
*
* If this ticket is running, this will always return false.
*/
bool HasResult();
/**
* @brief Get value, if any
*/
QVariant Get();
/**
* @brief Wait for ticket to be finished
*
* If this ticket is not running, this function returns immediately.
*/
void WaitForFinished();
void WaitForFinished(QMutex* mutex);
/**
* @brief Access this ticket's mutex
*
* Use if you're doing several operations on a ticket and need to ensure thread safety while
* doing so. Most of the time this isn't necessary since all functions are thread safe by default.
*/
QMutex* lock()
{
return &lock_;
}
/**
* @brief Signal to the ticket that it is running
*
* If any value is set, it is cleared.
*/
void Start();
/**
* @brief Finish ticket with no value
*
* Sets ticket to no longer running and assume it has received no result.
*/
void Finish();
/**
* @brief Finish ticket with value
*
* Sets ticket to no longer running and provide a value generated by the operation requested.
*/
void Finish(QVariant result);
signals:
/**
* @brief Emitted when finish has been called by any means (either cancelled or with a result)
*/
void Finished();
private:
void FinishInternal(bool has_result, QVariant result);
bool is_running_;
QVariant result_;
bool has_result_;
int finish_count_;
QMutex lock_;
QWaitCondition wait_;
};
using RenderTicketPtr = std::shared_ptr<RenderTicket>;
class RenderTicketWatcher : public QObject
{
Q_OBJECT
public:
RenderTicketWatcher(QObject* parent = nullptr);
RenderTicketPtr GetTicket() const
{
return ticket_;
}
void SetTicket(RenderTicketPtr ticket);
bool IsRunning();
void WaitForFinished();
QVariant Get();
bool HasResult();
void Cancel();
signals:
void Finished(RenderTicketWatcher* watcher);
private:
RenderTicketPtr ticket_;
private slots:
void TicketFinished();
};
}
Q_DECLARE_METATYPE(olive::RenderTicketPtr)
#endif // RENDERTICKET_H
+9
View File
@@ -200,6 +200,15 @@ int VideoParams::GetBytesPerPixel(VideoParams::Format format, int channels)
return GetBytesPerChannel(format) * channels;
}
QString VideoParams::GetNameForDivider(int div)
{
if (div == 1) {
return QCoreApplication::translate("VideoParams", "Full");
} else {
return QCoreApplication::translate("VideoParams", "1/%1").arg(div);
}
}
bool VideoParams::FormatIsFloat(VideoParams::Format format)
{
switch (format) {
+2
View File
@@ -231,6 +231,8 @@ public:
return GetBufferSize(width_, height_, format_, channel_count_);
}
static QString GetNameForDivider(int div);
static bool FormatIsFloat(Format format);
static QString GetFormatName(Format format);