hold worker busy state in renderbackend rather than in renderworker

Workers run in different threads and the backends can poll whether the worker
is currently busy or not. However the previous iteration has the worker (and an
atomic int) provide the busy state which could easily desync with the main
thread (since all workers run in different threads). By holding the busy states
in the main thread, the main thread will always be able to poll the busy state
accurately.
This commit is contained in:
itsmattkc
2019-12-06 19:38:57 +11:00
parent 5227e10f39
commit f6064b40df
9 changed files with 41 additions and 58 deletions
@@ -67,6 +67,8 @@ void AudioBackend::ConnectWorkerToThis(RenderWorker *worker)
void AudioBackend::ThreadCompletedCache(NodeDependency dep, NodeValueTable data)
{
SetWorkerBusyState(static_cast<RenderWorker*>(sender()), false);
QByteArray cached_samples = data.Get(NodeParam::kSamples).toByteArray();
int offset = params().time_to_bytes(dep.in());
+14 -14
View File
@@ -6,7 +6,8 @@
#include "functions.h"
OpenGLBackend::OpenGLBackend(QObject *parent) :
VideoRenderBackend(parent)
VideoRenderBackend(parent),
last_download_thread_(0)
{
}
@@ -135,6 +136,8 @@ bool OpenGLBackend::TimeIsCached(const TimeRange &time)
void OpenGLBackend::ThreadCompletedFrame(NodeDependency path, QByteArray hash, NodeValueTable table)
{
SetWorkerBusyState(static_cast<RenderWorker*>(sender()), false);
QVariant value = table.Get(NodeParam::kTexture);
OpenGLTexturePtr texture = value.value<OpenGLTexturePtr>();
@@ -146,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
@@ -184,6 +182,7 @@ void OpenGLBackend::ThreadCompletedDownload(NodeDependency dep, QByteArray hash)
void OpenGLBackend::ThreadSkippedFrame(NodeDependency dep, QByteArray hash)
{
frame_cache()->SetHash(dep.in(), hash);
SetWorkerBusyState(static_cast<RenderWorker*>(sender()), false);
// Queue up a new frame for this worker
CacheNext();
@@ -192,6 +191,7 @@ void OpenGLBackend::ThreadSkippedFrame(NodeDependency dep, QByteArray hash)
void OpenGLBackend::ThreadHashAlreadyExists(NodeDependency dep, QByteArray hash)
{
ThreadCompletedDownload(dep, hash);
SetWorkerBusyState(static_cast<RenderWorker*>(sender()), false);
// Queue up a new frame for this worker
CacheNext();
@@ -34,6 +34,8 @@ private:
OpenGLShaderCache shader_cache_;
int last_download_thread_;
private slots:
void ThreadCompletedFrame(NodeDependency path, QByteArray hash, NodeValueTable table);
void ThreadCompletedDownload(NodeDependency dep, QByteArray hash);
+17 -17
View File
@@ -240,7 +240,7 @@ void RenderBackend::CacheNext()
break;
}
if (worker->IsAvailable()) {
if (!WorkerIsBusy(worker)) {
TimeRange cache_frame = cache_queue_.takeFirst();
NodeDependency dep = NodeDependency(GetDependentInput()->get_connected_node(), cache_frame.in(), cache_frame.out());
@@ -249,6 +249,8 @@ void RenderBackend::CacheNext()
"Render",
Qt::QueuedConnection,
Q_ARG(NodeDependency, dep));
SetWorkerBusyState(worker, true);
}
}
}
@@ -288,10 +290,20 @@ 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 (RenderWorker* worker, processors_) {
if (!worker->IsAvailable()) {
foreach (bool busy, processor_busy_state_) {
if (busy) {
return false;
}
}
@@ -316,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
@@ -325,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()
+5 -2
View File
@@ -86,6 +86,9 @@ protected:
void UpdateNodeInputs();
bool WorkerIsBusy(RenderWorker* worker) const;
void SetWorkerBusyState(RenderWorker* worker, bool busy);
QList<TimeRange> cache_queue_;
QVector<RenderWorker*> processors_;
@@ -133,9 +136,9 @@ private:
bool recompile_queued_;
bool input_update_queued_;
private slots:
void ThreadRequestedSibling(NodeDependency dep);
QVector<bool> processor_busy_state_;
private slots:
void QueueRecompile();
};
-12
View File
@@ -6,16 +6,10 @@
RenderWorker::RenderWorker(QObject *parent) :
QObject(parent),
working_(0),
started_(false)
{
}
bool RenderWorker::IsAvailable()
{
return !working_;
}
bool RenderWorker::Init()
{
if (started_) {
@@ -51,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()) {
@@ -65,9 +56,6 @@ NodeValueTable RenderWorker::RenderAsSibling(NodeDependency dep)
// We're done!
// End this working state
working_--;
return value;
}
-6
View File
@@ -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:
@@ -56,8 +52,6 @@ protected:
virtual NodeValueTable RenderBlock(TrackOutput *track, const TimeRange& range) = 0;
QAtomicInt working_;
private:
bool started_;
-6
View File
@@ -18,8 +18,6 @@ const VideoRenderingParams &VideoRenderWorker::video_params()
NodeValueTable VideoRenderWorker::RenderInternal(const NodeDependency& path)
{
qDebug() << "Rendering" << path.in().toDouble() << "on" << this;
// Get hash of node graph
// We use SHA-1 for speed (benchmarks show it's the fastest hash available to us)
QCryptographicHash hasher(QCryptographicHash::Sha1);
@@ -141,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
@@ -164,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)
+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());