improved backend encapsulation

Various backend improvements are included in this commit, mostly for the
benefit of exporting. These include:
- Moving more non-GL code from OpenGL derivatives into base classes
- An "export mode" that changes the cache behavior of video backends
- Using the Viewer's UUID introduced a few commits ago
- No longer hardcoding the pixel format/render mode in the backend (since
  they'll inevitably differ when exporting vs previewing)
- Improved signalling for frames that are completed
This commit is contained in:
itsmattkc
2019-12-20 04:35:31 +11:00
parent 757aaa21c4
commit 8ace197d66
10 changed files with 142 additions and 109 deletions
+3
View File
@@ -21,6 +21,9 @@ add_subdirectory(vulkan)
set(OLIVE_SOURCES
${OLIVE_SOURCES}
render/backend/exporter.h
render/backend/exporter.cpp
render/backend/renderbackend.h
render/backend/renderbackend.cpp
render/backend/renderworker.h
+5 -3
View File
@@ -55,9 +55,6 @@ void AudioRenderBackend::ConnectViewer(ViewerOutput *node)
{
connect(node, SIGNAL(AudioChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&)));
connect(node, SIGNAL(AudioGraphChanged()), this, SLOT(QueueRecompile()));
// FIXME: Hardcoded format, though I doubt this will change any time soon (maybe we'll shift to DBL at some point)
SetParameters(AudioRenderingParams(node->audio_params(), SAMPLE_FMT_FLT));
}
void AudioRenderBackend::DisconnectViewer(ViewerOutput *node)
@@ -117,3 +114,8 @@ QString AudioRenderBackend::CachePathName()
return this_cache_dir.filePath(QStringLiteral("pcm"));
}
bool AudioRenderBackend::CanRender()
{
return params_.is_valid();
}
+2
View File
@@ -39,6 +39,8 @@ protected:
QString CachePathName();
virtual bool CanRender() override;
private:
void ValidateRanges();
+2
View File
@@ -22,6 +22,8 @@ set(OLIVE_SOURCES
render/backend/opengl/openglbackend.cpp
render/backend/opengl/openglcolorprocessor.h
render/backend/opengl/openglcolorprocessor.cpp
render/backend/opengl/openglexporter.h
render/backend/opengl/openglexporter.cpp
render/backend/opengl/openglframebuffer.h
render/backend/opengl/openglframebuffer.cpp
render/backend/opengl/openglshader.h
+8 -70
View File
@@ -6,8 +6,7 @@
#include "functions.h"
OpenGLBackend::OpenGLBackend(QObject *parent) :
VideoRenderBackend(parent),
last_download_thread_(0)
VideoRenderBackend(parent)
{
}
@@ -135,76 +134,15 @@ void OpenGLBackend::DecompileInternal()
shader_cache_.Clear();
}
bool OpenGLBackend::TimeIsQueued(const TimeRange &time)
void OpenGLBackend::EmitCachedFrameReady(const rational &time, const QVariant &value)
{
return cache_queue_.contains(time);
}
// FIXME: This texture is part of the texture cache and therefore volatile, we should probably copy it here instead
OpenGLTextureCache::ReferencePtr ref = value.value<OpenGLTextureCache::ReferencePtr>();
OpenGLTexturePtr tex = nullptr;
void OpenGLBackend::ThreadCompletedFrame(NodeDependency path, QByteArray hash, NodeValueTable table)
{
SetWorkerBusyState(static_cast<RenderWorker*>(sender()), false);
OpenGLTextureCache::ReferencePtr ref = table.Get(NodeParam::kTexture).value<OpenGLTextureCache::ReferencePtr>();
if (!ref) {
// No frame received, we set hash to an empty
frame_cache()->RemoveHash(path.in(), hash);
} else {
// Received a texture, let's download it
QString cache_fn = frame_cache()->CachePathName(hash);
// Find an available worker to download this texture
QMetaObject::invokeMethod(processors_.at(last_download_thread_%processors_.size()),
"Download",
Q_ARG(NodeDependency, path),
Q_ARG(QByteArray, hash),
Q_ARG(QVariant, QVariant::fromValue(ref)),
Q_ARG(QString, cache_fn));
last_download_thread_++;
if (ref) {
tex = ref->texture();
}
// Check if this frame has changed once again, in which case we may not want to draw it (it'll look jittery to the user)
if (!TimeIsQueued(TimeRange(path.in(), path.in()))) {
// FIXME: This texture is part of the texture cache and therefore volatile, we should probably copy it here instead
OpenGLTexturePtr tex = nullptr;
if (ref) {
tex = ref->texture();
}
emit CachedFrameReady(path.in(), QVariant::fromValue(tex));
}
// Queue up a new frame for this worker
CacheNext();
}
void OpenGLBackend::ThreadCompletedDownload(NodeDependency dep, QByteArray hash)
{
frame_cache()->SetHash(dep.in(), hash);
// 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(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();
}
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();
emit CachedFrameReady(time, QVariant::fromValue(tex));
}
+2 -10
View File
@@ -28,23 +28,15 @@ protected:
virtual void DecompileInternal() override;
private:
bool TimeIsQueued(const TimeRange &time);
virtual void EmitCachedFrameReady(const rational& time, const QVariant& value) override;
private:
OpenGLTexturePtr master_texture_;
OpenGLShaderCache shader_cache_;
OpenGLTextureCache texture_cache_;
int last_download_thread_;
private slots:
void ThreadCompletedFrame(NodeDependency path, QByteArray hash, NodeValueTable table);
void ThreadCompletedDownload(NodeDependency dep, QByteArray hash);
void ThreadSkippedFrame(NodeDependency dep, QByteArray hash);
void ThreadHashAlreadyExists(NodeDependency dep, QByteArray hash);
};
#endif // OPENGLBACKEND_H
+11 -13
View File
@@ -90,17 +90,11 @@ void RenderBackend::SetViewerNode(ViewerOutput *viewer_node)
if (viewer_node_ != nullptr) {
ConnectViewer(viewer_node_);
RegenerateCacheID();
}
}
void RenderBackend::SetCacheName(const QString &s)
{
cache_name_ = s;
cache_time_ = QDateTime::currentMSecsSinceEpoch();
RegenerateCacheID();
}
bool RenderBackend::IsInitiated()
{
return started_;
@@ -170,22 +164,25 @@ void RenderBackend::RegenerateCacheID()
{
QCryptographicHash hash(QCryptographicHash::Sha1);
if (cache_name_.isEmpty()
|| !cache_time_
if (!viewer_node_
|| !GenerateCacheIDInternal(hash)) {
cache_id_.clear();
CacheIDChangedEvent(QString());
return;
}
hash.addData(cache_name_.toUtf8());
hash.addData(QString::number(cache_time_).toUtf8());
hash.addData(viewer_node_->uuid().toByteArray());
QByteArray bytes = hash.result();
cache_id_ = bytes.toHex();
CacheIDChangedEvent(cache_id_);
}
bool RenderBackend::CanRender()
{
return true;
}
rational RenderBackend::GetSequenceLength()
{
if (viewer_node_ == nullptr) {
@@ -214,7 +211,8 @@ void RenderBackend::CacheNext()
{
if (!Init()
|| cache_queue_.isEmpty()
|| !ViewerIsConnected()) {
|| !ViewerIsConnected()
|| !CanRender()) {
return;
}
+4 -6
View File
@@ -25,10 +25,10 @@ public:
void SetViewerNode(ViewerOutput* viewer_node);
void SetCacheName(const QString& s);
bool IsInitiated();
ViewerOutput* viewer_node() const;
public slots:
virtual void InvalidateCache(const rational &start_range, const rational &end_range);
@@ -47,6 +47,8 @@ protected:
virtual void DecompileInternal() = 0;
virtual bool CanRender();
rational GetSequenceLength();
const QVector<QThread*>& threads();
@@ -76,8 +78,6 @@ protected:
virtual void ConnectWorkerToThis(RenderWorker* worker) = 0;
ViewerOutput* viewer_node() const;
bool ViewerIsConnected() const;
const QString& cache_id() const;
@@ -121,8 +121,6 @@ private:
*/
QString error_;
QString cache_name_;
qint64 cache_time_;
QString cache_id_;
QList<Node*> source_node_list_;
+89 -6
View File
@@ -31,10 +31,10 @@
#include "videorenderworker.h"
VideoRenderBackend::VideoRenderBackend(QObject *parent) :
RenderBackend(parent)
RenderBackend(parent),
export_mode_(false),
last_download_thread_(0)
{
// FIXME: Cache name should actually be the name of the sequence
SetCacheName("Test");
}
void VideoRenderBackend::InvalidateCache(const rational &start_range, const rational &end_range)
@@ -122,9 +122,6 @@ void VideoRenderBackend::ConnectViewer(ViewerOutput *node)
{
connect(node, SIGNAL(VideoChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&)));
connect(node, SIGNAL(VideoGraphChanged()), this, SLOT(QueueRecompile()));
// FIXME: Hardcoded format, mode, and divider
SetParameters(VideoRenderingParams(node->video_params(), olive::PIX_FMT_RGBA16F, olive::kOffline, 2));
}
void VideoRenderBackend::DisconnectViewer(ViewerOutput *node)
@@ -154,6 +151,11 @@ void VideoRenderBackend::SetParameters(const VideoRenderingParams& params)
RegenerateCacheID();
}
void VideoRenderBackend::SetExportMode(bool enabled)
{
export_mode_ = enabled;
}
bool VideoRenderBackend::GenerateCacheIDInternal(QCryptographicHash& hash)
{
if (!params_.is_valid()) {
@@ -234,3 +236,84 @@ NodeInput *VideoRenderBackend::GetDependentInput()
{
return viewer_node()->texture_input();
}
bool VideoRenderBackend::CanRender()
{
return params_.is_valid();
}
void VideoRenderBackend::ThreadCompletedFrame(NodeDependency path, QByteArray hash, NodeValueTable table)
{
SetWorkerBusyState(static_cast<RenderWorker*>(sender()), false);
QVariant texture = table.Get(NodeParam::kTexture);
if (!export_mode_) {
if (texture.isNull()) {
// No frame received, we set hash to an empty
frame_cache()->RemoveHash(path.in(), hash);
} else {
// Received a texture, let's download it
QString cache_fn = frame_cache()->CachePathName(hash);
// Find an available worker to download this texture
QMetaObject::invokeMethod(processors_.at(last_download_thread_%processors_.size()),
"Download",
Q_ARG(NodeDependency, path),
Q_ARG(QByteArray, hash),
Q_ARG(QVariant, texture),
Q_ARG(QString, cache_fn));
last_download_thread_++;
}
}
// Check if this frame has changed once again, in which case we may not want to draw it (it'll look jittery to the user)
if (!TimeIsQueued(TimeRange(path.in(), path.in()))) {
EmitCachedFrameReady(path.in(), texture);
if (export_mode_) {
QList<rational> times_with_this_hash = frame_cache()->TimesWithHash(hash);
foreach (const rational& t, times_with_this_hash) {
EmitCachedFrameReady(t, texture);
}
}
}
// Queue up a new frame for this worker
CacheNext();
}
void VideoRenderBackend::ThreadCompletedDownload(NodeDependency dep, QByteArray hash)
{
frame_cache()->SetHash(dep.in(), hash);
// 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 VideoRenderBackend::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();
}
void VideoRenderBackend::ThreadHashAlreadyExists(NodeDependency dep, QByteArray hash)
{
ThreadCompletedDownload(dep, hash);
SetWorkerBusyState(static_cast<RenderWorker*>(sender()), false);
// Queue up a new frame for this worker
CacheNext();
}
bool VideoRenderBackend::TimeIsQueued(const TimeRange &time)
{
return cache_queue_.contains(time);
}
+16 -1
View File
@@ -53,6 +53,8 @@ public:
*/
void SetParameters(const VideoRenderingParams &params);
void SetExportMode(bool enabled);
public slots:
virtual void InvalidateCache(const rational &start_range, const rational &end_range) override;
@@ -80,6 +82,8 @@ protected:
virtual NodeInput* GetDependentInput() override;
virtual bool CanRender() override;
VideoRenderFrameCache* frame_cache();
const VideoRenderingParams& params() const;
@@ -93,11 +97,17 @@ protected:
virtual void ConnectWorkerToThis(RenderWorker* processor) override;
virtual void EmitCachedFrameReady(const rational& time, const QVariant& value) = 0;
bool export_mode_;
signals:
void CachedFrameReady(const rational& time, QVariant value);
void CachedTimeReady(const rational& time);
private:
bool TimeIsQueued(const TimeRange &time);
VideoRenderingParams params_;
QByteArray cache_frame_load_buffer_;
@@ -106,8 +116,13 @@ private:
rational last_time_requested_;
private slots:
int last_download_thread_;
private slots:
void ThreadCompletedFrame(NodeDependency path, QByteArray hash, NodeValueTable table);
void ThreadCompletedDownload(NodeDependency dep, QByteArray hash);
void ThreadSkippedFrame(NodeDependency dep, QByteArray hash);
void ThreadHashAlreadyExists(NodeDependency dep, QByteArray hash);
};