From 9da5e7c9be5890125c5841ddf51d4e2cf1226e34 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 22 Oct 2019 13:29:11 +1100 Subject: [PATCH] added sample retrieve function --- app/node/output/viewer/viewer.cpp | 5 + .../audio/audiorendererdownloadthread.cpp | 128 +++++++++++++++ .../audio/audiorendererdownloadthread.h | 49 ++++++ .../audio/audiorendererprocessthread.cpp | 155 ++++++++++++++++++ app/render/audio/audiorendererprocessthread.h | 71 ++++++++ 5 files changed, 408 insertions(+) create mode 100644 app/render/audio/audiorendererdownloadthread.cpp create mode 100644 app/render/audio/audiorendererdownloadthread.h create mode 100644 app/render/audio/audiorendererprocessthread.cpp create mode 100644 app/render/audio/audiorendererprocessthread.h diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index f28165b16..d99eba51e 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -89,6 +89,11 @@ RenderTexturePtr ViewerOutput::GetTexture(const rational &time) return texture_input_->get_value(time).value(); } +QByteArray ViewerOutput::GetSamples(const rational &in, const rational &out) +{ + return samples_input_->get_value(in, out).toByteArray(); +} + void ViewerOutput::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from) { Node::InvalidateCache(start_range, end_range, from); diff --git a/app/render/audio/audiorendererdownloadthread.cpp b/app/render/audio/audiorendererdownloadthread.cpp new file mode 100644 index 000000000..fc69ebeec --- /dev/null +++ b/app/render/audio/audiorendererdownloadthread.cpp @@ -0,0 +1,128 @@ +#include "audiorendererdownloadthread.h" + +#include +#include +#include + +#include "common/define.h" +#include "render/pixelservice.h" + +AudioRendererDownloadThread::AudioRendererDownloadThread(QOpenGLContext *share_ctx, + const int &width, + const int &height, + const int ÷r, + const olive::PixelFormat &format, + const olive::RenderMode &mode) : + AudioRendererThreadBase(share_ctx, width, height, divider, format, mode), + cancelled_(false) +{ +} + +void AudioRendererDownloadThread::Queue(RenderTexturePtr texture, const QString& fn, const QByteArray &hash) +{ + texture_queue_lock_.lock(); + + texture_queue_.append({texture, fn, hash}); + + wait_cond_.wakeAll(); + + texture_queue_lock_.unlock(); +} + +void AudioRendererDownloadThread::Cancel() +{ + cancelled_ = true; + + texture_queue_lock_.lock(); + wait_cond_.wakeAll(); + texture_queue_lock_.unlock(); + + wait(); +} + +void AudioRendererDownloadThread::ProcessLoop() +{ + QOpenGLFunctions* f = render_instance()->context()->functions(); + QOpenGLExtraFunctions* xf = render_instance()->context()->extraFunctions(); + + f->glGenFramebuffers(1, &read_buffer_); + + DownloadQueueEntry entry; + + int buffer_size = PixelService::GetBufferSize(render_instance()->format(), + render_instance()->width(), + render_instance()->height()); + + QVector data_buffer; + data_buffer.resize(buffer_size); + + PixelFormatInfo format_info = PixelService::GetPixelFormatInfo(render_instance()->format()); + + // Set up OIIO::ImageSpec for compressing cached images on disk + OIIO::ImageSpec spec(render_instance()->width(), render_instance()->height(), kRGBAChannels, format_info.oiio_desc); + spec.attribute("compression", "dwaa:200"); + + while (!cancelled_) { + // Check queue for textures to download (use mutex to prevent collisions) + texture_queue_lock_.lock(); + + while (texture_queue_.isEmpty()) { + // Main waiting condition + wait_cond_.wait(&texture_queue_lock_); + + if (cancelled_) { + break; + } + } + if (cancelled_) { + texture_queue_lock_.unlock(); + break; + } + + entry = texture_queue_.takeFirst(); + + texture_queue_lock_.unlock(); + + // Download the texture + + f->glBindFramebuffer(GL_READ_FRAMEBUFFER, read_buffer_); + + xf->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + entry.texture->texture(), + 0); + + f->glReadPixels(0, + 0, + entry.texture->width(), + entry.texture->height(), + format_info.pixel_format, + format_info.pixel_type, + data_buffer.data()); + + xf->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + 0, + 0); + + f->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + + std::string working_fn_std = entry.filename.toStdString(); + + std::unique_ptr out = OIIO::ImageOutput::create(working_fn_std); + + if (out) { + out->open(working_fn_std, spec); + out->write_image(format_info.oiio_desc, data_buffer.data()); + out->close(); + + emit Downloaded(entry.hash); + } else { + qWarning() << tr("Failed to open output file \"%1\"").arg(entry.filename); + } + } + + f->glDeleteFramebuffers(1, &read_buffer_); +} diff --git a/app/render/audio/audiorendererdownloadthread.h b/app/render/audio/audiorendererdownloadthread.h new file mode 100644 index 000000000..7cb2222c9 --- /dev/null +++ b/app/render/audio/audiorendererdownloadthread.h @@ -0,0 +1,49 @@ +#ifndef AUDIORENDERERDOWNLOADTHREAD_H +#define AUDIORENDERERDOWNLOADTHREAD_H + +#include "audiorendererthreadbase.h" + +class AudioRendererDownloadThread : public AudioRendererThreadBase +{ + Q_OBJECT +public: + AudioRendererDownloadThread(QOpenGLContext* share_ctx, + const int& width, + const int& height, + const int ÷r, + const olive::PixelFormat& format, + const olive::RenderMode& mode); + + void Queue(RenderTexturePtr texture, const QString &fn, const QByteArray &hash); + +public slots: + virtual void Cancel() override; + +signals: + void Downloaded(const QByteArray& hash); + +protected: + virtual void ProcessLoop() override; + +private: + struct DownloadQueueEntry { + RenderTexturePtr texture; + QString filename; + QByteArray hash; + }; + + GLuint read_buffer_; + + QVector texture_queue_; + + QMutex texture_queue_lock_; + + QAtomicInt cancelled_; + + QByteArray hash_; + +}; + +using AudioRendererDownloadThreadPtr = std::shared_ptr; + +#endif // AUDIORENDERERDOWNLOADTHREAD_H diff --git a/app/render/audio/audiorendererprocessthread.cpp b/app/render/audio/audiorendererprocessthread.cpp new file mode 100644 index 000000000..a25b1be7a --- /dev/null +++ b/app/render/audio/audiorendererprocessthread.cpp @@ -0,0 +1,155 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 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 . + +***/ + +#include "audiorendererprocessthread.h" + +#include "audiorenderer.h" + +AudioRendererProcessThread::AudioRendererProcessThread(AudioRendererProcessor* parent, + QOpenGLContext *share_ctx, + const int &width, + const int &height, + const int ÷r, + const olive::PixelFormat &format, + const olive::RenderMode &mode) : + AudioRendererThreadBase(share_ctx, width, height, divider, format, mode), + parent_(parent), + cancelled_(false) +{ + +} + +bool AudioRendererProcessThread::Queue(const NodeDependency& dep, bool wait, bool sibling) +{ + if (wait) { + // Wait for thread to be available + mutex_.lock(); + } else if (!mutex_.tryLock()) { + return false; + } + + // We can now change params without the other thread using them + path_ = dep; + sibling_ = sibling; + + // Prepare to wait for thread to respond + caller_mutex_.lock(); + + // Wake up our main thread + wait_cond_.wakeAll(); + mutex_.unlock(); + + // Wait for thread to start before returning + wait_cond_.wait(&caller_mutex_); + caller_mutex_.unlock(); + + return true; +} + +void AudioRendererProcessThread::Cancel() +{ + cancelled_ = true; + + mutex_.lock(); + wait_cond_.wakeAll(); + mutex_.unlock(); + + wait(); +} + +void AudioRendererProcessThread::ProcessLoop() +{ + while (!cancelled_) { + // Main waiting condition + wait_cond_.wait(&mutex_); + + if (cancelled_) { + break; + } + + // Wake up main thread + caller_mutex_.lock(); + wait_cond_.wakeAll(); + caller_mutex_.unlock(); + + // Process the Node + NodeOutput* output_to_process = path_.node(); + Node* node_to_process = output_to_process->parent(); + + texture_ = nullptr; + + QList all_deps; + bool has_hash = false; + bool can_cache = true; + + if (!sibling_) { + node_to_process->Lock(); + + all_deps = node_to_process->GetDependencies(); + foreach (Node* dep, all_deps) { + dep->Lock(); + } + + // Check hash + QCryptographicHash hasher(QCryptographicHash::Sha1); + node_to_process->Hash(&hasher, output_to_process, path_.time()); + hash_ = hasher.result(); + + has_hash = parent_->HasHash(hash_); + can_cache = false; + } + + if (!has_hash){ + + if ((can_cache = parent_->TryCache(hash_))) { + + QList deps = node_to_process->RunDependencies(output_to_process, path_.time()); + + // Ask for other threads to run these deps while we're here + if (!deps.isEmpty()) { + for (int i=1;iget_value(path_.time()).value(); + + render_instance()->context()->functions()->glFinish(); + } + } + + if (!sibling_) { + foreach (Node* dep, all_deps) { + dep->Unlock(); + } + + node_to_process->Unlock(); + } + + if (can_cache) { + // We cached this frame, signal that it will need to be downloaded to disk + emit CachedFrame(texture_, path_.time(), hash_); + } else { + // This hash already exists, no need to cache, just map it + emit FrameSkipped(path_.time(), hash_); + } + } +} diff --git a/app/render/audio/audiorendererprocessthread.h b/app/render/audio/audiorendererprocessthread.h new file mode 100644 index 000000000..a02ab0643 --- /dev/null +++ b/app/render/audio/audiorendererprocessthread.h @@ -0,0 +1,71 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 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 . + +***/ + +#ifndef AUDIORENDERERPROCESSTHREAD_H +#define AUDIORENDERERPROCESSTHREAD_H + +#include "audiorendererthreadbase.h" + +class AudioRendererProcessor; + +class AudioRendererProcessThread : public AudioRendererThreadBase +{ + Q_OBJECT +public: + AudioRendererProcessThread(AudioRendererProcessor* parent, + QOpenGLContext* share_ctx, + const int& width, + const int& height, const int ÷r, + const olive::PixelFormat& format, + const olive::RenderMode& mode); + + bool Queue(const NodeDependency &dep, bool wait, bool sibling); + +public slots: + virtual void Cancel() override; + +protected: + virtual void ProcessLoop() override; + +signals: + void RequestSibling(NodeDependency dep); + + void CachedFrame(RenderTexturePtr texture, const rational& time, const QByteArray& hash); + + void FrameSkipped(const rational& time, const QByteArray& hash); + +private: + AudioRendererProcessor* parent_; + + NodeDependency path_; + + QByteArray hash_; + + RenderTexturePtr texture_; + + QAtomicInt cancelled_; + + bool sibling_; + +}; + +using AudioRendererProcessThreadPtr = std::shared_ptr; + +#endif // AUDIORENDERERPROCESSTHREAD_H