diff --git a/app/render/audioparams.cpp b/app/render/audioparams.cpp index 660d3a16c..ea04d90e3 100644 --- a/app/render/audioparams.cpp +++ b/app/render/audioparams.cpp @@ -27,23 +27,23 @@ const uint64_t &AudioParams::channel_layout() const } AudioRenderingParams::AudioRenderingParams() : - format_(olive::SAMPLE_FMT_INVALID) + format_(SAMPLE_FMT_INVALID) { } -AudioRenderingParams::AudioRenderingParams(const int &sample_rate, const uint64_t &channel_layout, const olive::SampleFormat &format) : +AudioRenderingParams::AudioRenderingParams(const int &sample_rate, const uint64_t &channel_layout, const SampleFormat &format) : AudioParams(sample_rate, channel_layout), format_(format) { } -AudioRenderingParams::AudioRenderingParams(const AudioParams ¶ms, const olive::SampleFormat &format) : +AudioRenderingParams::AudioRenderingParams(const AudioParams ¶ms, const SampleFormat &format) : AudioParams(params), format_(format) { } -const olive::SampleFormat &AudioRenderingParams::format() const +const SampleFormat &AudioRenderingParams::format() const { return format_; } @@ -52,7 +52,14 @@ int AudioRenderingParams::time_to_bytes(const rational &time) const { Q_ASSERT(is_valid()); - return qFloor(time.toDouble() * sample_rate()) * channel_count() * bytes_per_sample_per_channel(); + return time_to_samples(time) * channel_count() * bytes_per_sample_per_channel(); +} + +int AudioRenderingParams::time_to_samples(const rational &time) const +{ + Q_ASSERT(is_valid()); + + return qFloor(time.toDouble() * sample_rate()); } int AudioRenderingParams::samples_to_bytes(const int &samples) const @@ -70,18 +77,18 @@ int AudioRenderingParams::channel_count() const int AudioRenderingParams::bytes_per_sample_per_channel() const { switch (format_) { - case olive::SAMPLE_FMT_U8: + case SAMPLE_FMT_U8: return 1; - case olive::SAMPLE_FMT_S16: + case SAMPLE_FMT_S16: return 2; - case olive::SAMPLE_FMT_S32: - case olive::SAMPLE_FMT_FLT: + case SAMPLE_FMT_S32: + case SAMPLE_FMT_FLT: return 4; - case olive::SAMPLE_FMT_DBL: - case olive::SAMPLE_FMT_S64: + case SAMPLE_FMT_DBL: + case SAMPLE_FMT_S64: return 8; - case olive::SAMPLE_FMT_INVALID: - case olive::SAMPLE_FMT_COUNT: + case SAMPLE_FMT_INVALID: + case SAMPLE_FMT_COUNT: break; } @@ -95,8 +102,14 @@ int AudioRenderingParams::bits_per_sample() const bool AudioRenderingParams::is_valid() const { - return (sample_rate() > 0 - && channel_layout() > 0 - && format_ != olive::SAMPLE_FMT_INVALID - && format_ != olive::SAMPLE_FMT_COUNT); + bool valid = (sample_rate() > 0 + && channel_layout() > 0 + && format_ != SAMPLE_FMT_INVALID + && format_ != SAMPLE_FMT_COUNT); + + if (!valid) { + qWarning() << "Invalid params found:" << sample_rate() << channel_layout() << format(); + } + + return valid; } diff --git a/app/render/audioparams.h b/app/render/audioparams.h index a2fc1bbda..043f1b5ca 100644 --- a/app/render/audioparams.h +++ b/app/render/audioparams.h @@ -25,20 +25,21 @@ private: class AudioRenderingParams : public AudioParams { public: AudioRenderingParams(); - AudioRenderingParams(const int& sample_rate, const uint64_t& channel_layout, const olive::SampleFormat& format); - AudioRenderingParams(const AudioParams& params, const olive::SampleFormat& format); + AudioRenderingParams(const int& sample_rate, const uint64_t& channel_layout, const SampleFormat& format); + AudioRenderingParams(const AudioParams& params, const SampleFormat& format); int time_to_bytes(const rational& time) const; + int time_to_samples(const rational& time) const; int samples_to_bytes(const int& samples) const; int channel_count() const; int bytes_per_sample_per_channel() const; int bits_per_sample() const; bool is_valid() const; - const olive::SampleFormat& format() const; + const SampleFormat& format() const; private: - olive::SampleFormat format_; + SampleFormat format_; }; #endif // AUDIOPARAMS_H diff --git a/app/render/backend/audio/CMakeLists.txt b/app/render/backend/audio/CMakeLists.txt index 5fcb98547..85ec7b702 100644 --- a/app/render/backend/audio/CMakeLists.txt +++ b/app/render/backend/audio/CMakeLists.txt @@ -18,5 +18,7 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} render/backend/audio/audiobackend.h render/backend/audio/audiobackend.cpp + render/backend/audio/audioworker.h + render/backend/audio/audioworker.cpp PARENT_SCOPE ) diff --git a/app/render/backend/audio/audiobackend.cpp b/app/render/backend/audio/audiobackend.cpp index a19f186f8..4336ceff4 100644 --- a/app/render/backend/audio/audiobackend.cpp +++ b/app/render/backend/audio/audiobackend.cpp @@ -1,13 +1,34 @@ #include "audiobackend.h" +#include "audioworker.h" + AudioBackend::AudioBackend(QObject *parent) : AudioRenderBackend(parent) { } +AudioBackend::~AudioBackend() +{ + Close(); +} + +QIODevice *AudioBackend::GetAudioPullDevice() +{ + pull_device_.setFileName(CachePathName()); + + return &pull_device_; +} + bool AudioBackend::InitInternal() { - // This backend doesn't init anything yet + // Initiate one thread per CPU core + for (int i=0;iSetParameters(params()); + processors_.append(processor); + } + return true; } @@ -26,3 +47,34 @@ void AudioBackend::DecompileInternal() { // This backend doesn't compile anything yet } + +void AudioBackend::ConnectWorkerToThis(RenderWorker *worker) +{ + connect(worker, SIGNAL(CompletedCache(NodeDependency)), this, SLOT(ThreadCompletedCache(NodeDependency))); +} + +void AudioBackend::ThreadCompletedCache(NodeDependency dep) +{ + caching_ = false; + + QByteArray cached_samples = dep.node()->get_cached_value(dep.range()).toByteArray(); + + int offset = params().time_to_bytes(dep.in()); + int length = params().time_to_bytes(dep.range().length()); + int out_point = offset + length; + + if (pcm_data_.size() < out_point) { + pcm_data_.resize(out_point); + } + + // Replace data with this data + memcpy(pcm_data_.data() + offset, cached_samples.data(), static_cast(length)); + + QFile f(CachePathName()); + if (f.open(QFile::WriteOnly)) { + f.write(pcm_data_); + f.close(); + } + + CacheNext(); +} diff --git a/app/render/backend/audio/audiobackend.h b/app/render/backend/audio/audiobackend.h index f21a7cac8..7cbe4c428 100644 --- a/app/render/backend/audio/audiobackend.h +++ b/app/render/backend/audio/audiobackend.h @@ -1,6 +1,8 @@ #ifndef AUDIOBACKEND_H #define AUDIOBACKEND_H +#include + #include "../audiorenderbackend.h" class AudioBackend : public AudioRenderBackend @@ -9,6 +11,10 @@ class AudioBackend : public AudioRenderBackend public: AudioBackend(QObject* parent = nullptr); + virtual ~AudioBackend() override; + + virtual QIODevice* GetAudioPullDevice() override; + protected: virtual bool InitInternal() override; @@ -18,7 +24,13 @@ protected: virtual void DecompileInternal() override; + virtual void ConnectWorkerToThis(RenderWorker* worker) override; +private slots: + void ThreadCompletedCache(NodeDependency dep); + +private: + QFile pull_device_; }; diff --git a/app/render/backend/audio/audioworker.cpp b/app/render/backend/audio/audioworker.cpp index 125e249b8..b7068bc4c 100644 --- a/app/render/backend/audio/audioworker.cpp +++ b/app/render/backend/audio/audioworker.cpp @@ -1,6 +1,23 @@ #include "audioworker.h" -AudioWorker::AudioWorker() +AudioWorker::AudioWorker(DecoderCache *decoder_cache, QObject *parent) : + AudioRenderWorker(decoder_cache, parent) { +} +QVariant AudioWorker::FrameToValue(FramePtr frame) +{ + return frame->ToByteArray(); +} + +bool AudioWorker::OutputIsAccelerated(NodeOutput *output) +{ + Q_UNUSED(output) + return false; +} + +QVariant AudioWorker::RunNodeAccelerated(NodeOutput *output) +{ + Q_UNUSED(output) + return QVariant(); } diff --git a/app/render/backend/audio/audioworker.h b/app/render/backend/audio/audioworker.h index 76cea9f30..b8c42f956 100644 --- a/app/render/backend/audio/audioworker.h +++ b/app/render/backend/audio/audioworker.h @@ -1,11 +1,22 @@ #ifndef AUDIOWORKER_H #define AUDIOWORKER_H +#include "../audiorenderworker.h" -class AudioWorker +class AudioWorker : public AudioRenderWorker { public: - AudioWorker(); + AudioWorker(DecoderCache* decoder_cache, QObject* parent = nullptr); + +protected: + virtual QVariant FrameToValue(FramePtr frame) override; + + virtual bool OutputIsAccelerated(NodeOutput *output) override; + + virtual QVariant RunNodeAccelerated(NodeOutput *output) override; + +private: + }; #endif // AUDIOWORKER_H diff --git a/app/render/backend/audiorenderbackend.cpp b/app/render/backend/audiorenderbackend.cpp index 2c3fe61aa..06955f286 100644 --- a/app/render/backend/audiorenderbackend.cpp +++ b/app/render/backend/audiorenderbackend.cpp @@ -1,11 +1,13 @@ #include "audiorenderbackend.h" +#include #include +#include "common/filefunctions.h" + AudioRenderBackend::AudioRenderBackend(QObject *parent) : RenderBackend(parent) { - } void AudioRenderBackend::SetParameters(const AudioRenderingParams ¶ms) @@ -23,8 +25,17 @@ void AudioRenderBackend::SetParameters(const AudioRenderingParams ¶ms) void AudioRenderBackend::InvalidateCache(const rational &start_range, const rational &end_range) { + rational start_range_adj = qMax(rational(0), start_range); + rational end_range_adj = qMin(viewer_node()->Length(), end_range); + + // Truncate to length if necessary + int max_length_in_bytes = params().time_to_bytes(viewer_node()->Length()); + if (pcm_data_.size() > max_length_in_bytes) { + pcm_data_.resize(max_length_in_bytes); + } + // Add the range to the list - cache_queue_.append(TimeRange(start_range, end_range)); + cache_queue_.append(TimeRange(start_range_adj, end_range_adj)); // Remove any overlaps so we don't render the same thing twice ValidateRanges(); @@ -37,7 +48,7 @@ void AudioRenderBackend::ViewerNodeChangedEvent(ViewerOutput *node) { if (node != nullptr) { // FIXME: Hardcoded format - SetParameters(AudioRenderingParams(node->audio_params(), olive::SAMPLE_FMT_FLT)); + SetParameters(AudioRenderingParams(node->audio_params(), SAMPLE_FMT_FLT)); } } @@ -55,6 +66,16 @@ bool AudioRenderBackend::GenerateCacheIDInternal(QCryptographicHash &hash) return true; } +const AudioRenderingParams &AudioRenderBackend::params() +{ + return params_; +} + +NodeInput *AudioRenderBackend::GetDependentInput() +{ + return viewer_node()->samples_input(); +} + void AudioRenderBackend::ValidateRanges() { for (int i=0;i b.out()); + return !(a.out() < b.in() || a.in() > b.out()); } diff --git a/app/render/backend/audiorenderbackend.h b/app/render/backend/audiorenderbackend.h index e2f0d87ca..982bdbca1 100644 --- a/app/render/backend/audiorenderbackend.h +++ b/app/render/backend/audiorenderbackend.h @@ -18,6 +18,10 @@ public: */ void SetParameters(const AudioRenderingParams ¶ms); + virtual QIODevice* GetAudioPullDevice() = 0; + + const AudioRenderingParams& params(); + public slots: virtual void InvalidateCache(const rational &start_range, const rational &end_range) override; @@ -29,7 +33,11 @@ protected: */ virtual bool GenerateCacheIDInternal(QCryptographicHash& hash) override; - //virtual void CacheIDChangedEvent(const QString& id) override; + virtual NodeInput* GetDependentInput() override; + + QString CachePathName(); + + QByteArray pcm_data_; private: void ValidateRanges(); @@ -40,8 +48,6 @@ private: AudioRenderingParams params_; - QByteArray pcm_data_; - }; #endif // AUDIORENDERBACKEND_H diff --git a/app/render/backend/audiorenderworker.cpp b/app/render/backend/audiorenderworker.cpp index 21b9934fb..403ecf73c 100644 --- a/app/render/backend/audiorenderworker.cpp +++ b/app/render/backend/audiorenderworker.cpp @@ -1,13 +1,54 @@ #include "audiorenderworker.h" +#include "audio/audiomanager.h" + AudioRenderWorker::AudioRenderWorker(DecoderCache *decoder_cache, QObject *parent) : RenderWorker(decoder_cache, parent) { } +void AudioRenderWorker::SetParameters(const AudioRenderingParams &audio_params) +{ + audio_params_ = audio_params; +} + void AudioRenderWorker::RenderAsSibling(NodeDependency dep) { + NodeOutput* output = dep.node(); + Node* node = output->parent(); + QList connected_inputs; + QVariant value; + // Set working state + working_++; + + bool locked = false; + + // 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->IsBlock() + && (dep.range().in() < static_cast(node)->in() + || dep.range().out() > static_cast(node)->out())) { + // If the range is not wholly contained in this Block, we'll need to do some extra processing + value = RenderBlock(output, dep.range()); + } else { + node->LockProcessing(); + value = ProcessNodeNormally(NodeDependency(output, dep.range())); + locked = true; + } + + if (!locked) { + node->LockProcessing(); + } + + // Place the value into the output + output->cache_value(dep.range(), value); + + // We're done! + node->UnlockProcessing(); + + // End this working state + working_--; } bool AudioRenderWorker::InitInternal() @@ -20,3 +61,59 @@ void AudioRenderWorker::CloseInternal() { // Nothing to init yet } + +QVariant AudioRenderWorker::RenderBlock(NodeOutput* output, const TimeRange &range) +{ + QList active_blocks = ValidateBlockRange(static_cast(output->parent()), range); + + // All these blocks will need to output to a buffer so we create one here + QByteArray block_range_buffer(audio_params_.time_to_bytes(range.length()), 0); + + // Loop through active blocks retrieving their audio + while (!active_blocks.isEmpty()) { + int block_for_this_thread = -1; + + for (int i=0;i(b->GetParameterWithID(output->id())); + + TimeRange range_for_block(qMax(b->in(), range.in()), + qMin(b->out(), range.out())); + + // If the block is locked, we assume another thread has it. Otherwise, we'll work with it + if (connected_output->has_cached_value(range_for_block)) { + // This output already has this value, no need to process it again + QByteArray samples_from_this_block = connected_output->get_cached_value(range_for_block).toByteArray(); + + int destination_offset = audio_params_.time_to_bytes(range_for_block.in() - range.in()); + + memcpy(block_range_buffer.data()+destination_offset, + samples_from_this_block.data(), + static_cast(samples_from_this_block.size())); + + active_blocks.removeAt(i); + i--; + } else if (!b->IsProcessingLocked()) { + if (block_for_this_thread == -1) { + block_for_this_thread = i; + } else { + emit RequestSibling(NodeDependency(connected_output, + range_for_block)); + } + } + } + + if (block_for_this_thread > -1) { + Block* b = active_blocks.at(block_for_this_thread); + TimeRange range_for_block(qMax(b->in(), range.in()), + qMin(b->out(), range.out())); + + RenderAsSibling(NodeDependency(static_cast(b->GetParameterWithID(output->id())), + range_for_block)); + } else { + QThread::msleep(500); + } + } + + return block_range_buffer; +} diff --git a/app/render/backend/audiorenderworker.h b/app/render/backend/audiorenderworker.h index 21d0e6b6f..46bb351d6 100644 --- a/app/render/backend/audiorenderworker.h +++ b/app/render/backend/audiorenderworker.h @@ -9,6 +9,8 @@ class AudioRenderWorker : public RenderWorker public: AudioRenderWorker(DecoderCache* decoder_cache, QObject* parent = nullptr); + void SetParameters(const AudioRenderingParams& audio_params); + public slots: virtual void RenderAsSibling(NodeDependency dep) override; @@ -17,7 +19,10 @@ protected: virtual void CloseInternal() override; + QVariant RenderBlock(NodeOutput *output, const TimeRange& range); +private: + AudioRenderingParams audio_params_; }; diff --git a/app/render/backend/opengl/openglbackend.cpp b/app/render/backend/opengl/openglbackend.cpp index 9d8ed95a0..560fa8973 100644 --- a/app/render/backend/opengl/openglbackend.cpp +++ b/app/render/backend/opengl/openglbackend.cpp @@ -14,7 +14,7 @@ OpenGLBackend::OpenGLBackend(QObject *parent) : OpenGLBackend::~OpenGLBackend() { - CloseInternal(); + Close(); } bool OpenGLBackend::InitInternal() @@ -32,27 +32,10 @@ bool OpenGLBackend::InitInternal() // Initiate one thread per CPU core for (int i=0;iSetParameters(params()); - - // Connect to it - connect(processor, SIGNAL(RequestSibling(NodeDependency)), this, SLOT(ThreadRequestedSibling(NodeDependency))); - connect(processor, SIGNAL(CompletedFrame(NodeDependency, QByteArray)), this, SLOT(ThreadCompletedFrame(NodeDependency, QByteArray))); - connect(processor, SIGNAL(HashAlreadyBeingCached()), this, SLOT(ThreadSkippedFrame())); - connect(processor, SIGNAL(CompletedDownload(NodeDependency, QByteArray)), this, SLOT(ThreadCompletedDownload(NodeDependency, QByteArray))); - connect(processor, SIGNAL(HashAlreadyExists(NodeDependency, QByteArray)), this, SLOT(ThreadHashAlreadyExists(NodeDependency, QByteArray))); - - // Finally, we can move it to its own thread - processor->moveToThread(thread); - - // Add processor to list processors_.append(processor); - - // This function blocks the main thread intentionally. See the documentation for this function to see why. - processor->Init(); } // Create master texture (the one sent to the viewer) @@ -77,8 +60,6 @@ void OpenGLBackend::CloseInternal() master_texture_ = nullptr; push_texture_ = nullptr; //copy_pipeline_ = nullptr; - - VideoRenderBackend::Close(); } OpenGLTexturePtr OpenGLBackend::GetCachedFrameAsTexture(const rational &time) @@ -114,7 +95,7 @@ bool OpenGLBackend::CompileInternal() bool ret = TraverseCompiling(viewer_node()); if (ret) { - qDebug() << "Compiled successfully!"; + //qDebug() << "Compiled successfully!"; compiled_ = true; } else { qDebug() << "Compile failed:" << GetError(); @@ -173,7 +154,7 @@ bool OpenGLBackend::TraverseCompiling(Node *n) shader_cache_.AddShader(connected_output, program); - qDebug() << "Compiled" << connected_output->parent()->id() << "->" << connected_output->id(); + //qDebug() << "Compiled" << connected_output->parent()->id() << "->" << connected_output->id(); } } @@ -271,20 +252,6 @@ void OpenGLBackend::ThreadCompletedFrame(NodeDependency path, QByteArray hash) CacheNext(); }*/ -void OpenGLBackend::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; - } - } -} - void OpenGLBackend::ThreadCompletedDownload(NodeDependency dep, QByteArray hash) { frame_cache()->SetHash(dep.in(), hash); diff --git a/app/render/backend/opengl/openglbackend.h b/app/render/backend/opengl/openglbackend.h index 51d414909..36e50408a 100644 --- a/app/render/backend/opengl/openglbackend.h +++ b/app/render/backend/opengl/openglbackend.h @@ -41,7 +41,6 @@ private: private slots: void ThreadCompletedFrame(NodeDependency path, QByteArray hash); - void ThreadRequestedSibling(NodeDependency dep); void ThreadCompletedDownload(NodeDependency dep, QByteArray hash); void ThreadSkippedFrame(); void ThreadHashAlreadyExists(NodeDependency dep, QByteArray hash); diff --git a/app/render/backend/opengl/openglworker.cpp b/app/render/backend/opengl/openglworker.cpp index 686c5c9ec..36a61f875 100644 --- a/app/render/backend/opengl/openglworker.cpp +++ b/app/render/backend/opengl/openglworker.cpp @@ -47,7 +47,7 @@ bool OpenGLWorker::InitInternal() return true; } -QVariant OpenGLWorker::FrameToTexture(FramePtr frame) +QVariant OpenGLWorker::FrameToValue(FramePtr frame) { OpenGLTexturePtr footage_tex = std::make_shared(); footage_tex->Create(ctx_, frame); @@ -63,7 +63,7 @@ QVariant OpenGLWorker::FrameToTexture(FramePtr frame) return QVariant::fromValue(footage_tex); } -bool OpenGLWorker::OutputIsShader(NodeOutput* output) +bool OpenGLWorker::OutputIsAccelerated(NodeOutput* output) { return shader_cache_->HasShader(output); } @@ -83,7 +83,7 @@ void OpenGLWorker::ParametersChangedEvent() } } -QVariant OpenGLWorker::RunNodeAsShader(NodeOutput *out) +QVariant OpenGLWorker::RunNodeAccelerated(NodeOutput *out) { OpenGLShaderPtr shader = shader_cache_->GetShader(out); Node* node = out->parent(); @@ -126,6 +126,7 @@ QVariant OpenGLWorker::RunNodeAsShader(NodeOutput *out) shader->setUniformValue(variable_location, input->value().value()); break; case NodeInput::kMatrix: + qDebug() << "Setting uniform value to matrix" << input->value().value(); shader->setUniformValue(variable_location, input->value().value()); break; case NodeInput::kColor: @@ -138,14 +139,14 @@ QVariant OpenGLWorker::RunNodeAsShader(NodeOutput *out) case NodeInput::kFootage: { OpenGLTexturePtr texture = input->value().value(); - qDebug() << " Binding" << texture->texture() << "from" << input << "to GL_TEXTURE" << input_texture_count; + //qDebug() << " Binding" << texture->texture() << "from" << input << "to GL_TEXTURE" << input_texture_count; functions_->glActiveTexture(GL_TEXTURE0 + input_texture_count); functions_->glBindTexture(GL_TEXTURE_2D, texture->texture()); // Set value to bound texture shader->setUniformValue(variable_location, input_texture_count); - qDebug() << " Setting" << input->id() << "to" << input_texture_count; + //qDebug() << " Setting" << input->id() << "to" << input_texture_count; input_texture_count++; break; @@ -165,7 +166,7 @@ QVariant OpenGLWorker::RunNodeAsShader(NodeOutput *out) } } - qDebug() << " Blitting with shader!"; + //qDebug() << " Blitting with shader!"; olive::gl::Blit(shader); // Release any textures we bound before diff --git a/app/render/backend/opengl/openglworker.h b/app/render/backend/opengl/openglworker.h index 2702f61c3..556efb6da 100644 --- a/app/render/backend/opengl/openglworker.h +++ b/app/render/backend/opengl/openglworker.h @@ -45,11 +45,11 @@ protected: virtual void CloseInternal() override; - virtual QVariant FrameToTexture(FramePtr frame) override; + virtual QVariant FrameToValue(FramePtr frame) override; - virtual bool OutputIsShader(NodeOutput *output) override; + virtual bool OutputIsAccelerated(NodeOutput *output) override; - virtual QVariant RunNodeAsShader(NodeOutput *output) override; + virtual QVariant RunNodeAccelerated(NodeOutput *output) override; virtual void TextureToBuffer(const QVariant& texture, QByteArray& buffer) override; diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp index 12af1eb5c..9efbd62a3 100644 --- a/app/render/backend/renderbackend.cpp +++ b/app/render/backend/renderbackend.cpp @@ -10,11 +10,6 @@ RenderBackend::RenderBackend(QObject *parent) : started_(false), viewer_node_(nullptr) { - -} - -RenderBackend::~RenderBackend() -{ } bool RenderBackend::Init() @@ -35,6 +30,9 @@ bool RenderBackend::Init() started_ = InitInternal(); + // Connects workers and moves them to their respective threads + InitWorkers(); + if (!started_) { Close(); } @@ -163,7 +161,7 @@ void RenderBackend::CacheNext() TimeRange cache_frame = cache_queue_.takeFirst(); - qDebug() << "Caching FRAME" << cache_frame.in() << "to" << cache_frame.out(); + //qDebug() << "Caching FRAME" << cache_frame.in() << "to" << cache_frame.out(); caching_ = GenerateData(cache_frame); } @@ -175,7 +173,7 @@ bool RenderBackend::GenerateData(const TimeRange &range) return false; } - NodeDependency dep = NodeDependency(viewer_node()->texture_input()->get_connected_output(), range.in(), range.out()); + NodeDependency dep = NodeDependency(GetDependentInput()->get_connected_output(), range.in(), range.out()); foreach (RenderWorker* worker, processors_) { if (worker->IsAvailable() || worker == processors_.last()) { @@ -214,3 +212,35 @@ void RenderBackend::CacheIDChangedEvent(const QString &id) { Q_UNUSED(id) } + +void RenderBackend::InitWorkers() +{ + for (int i=0;imoveToThread(thread); + + // 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; + } + } +} diff --git a/app/render/backend/renderbackend.h b/app/render/backend/renderbackend.h index d4c8467fa..6d928c10d 100644 --- a/app/render/backend/renderbackend.h +++ b/app/render/backend/renderbackend.h @@ -12,7 +12,6 @@ class RenderBackend : public QObject Q_OBJECT public: RenderBackend(QObject* parent = nullptr); - virtual ~RenderBackend() override; Q_DISABLE_COPY_MOVE(RenderBackend) @@ -68,6 +67,12 @@ protected: bool GenerateData(const TimeRange& range); + void InitWorkers(); + + virtual NodeInput* GetDependentInput() = 0; + + virtual void ConnectWorkerToThis(RenderWorker* worker) = 0; + ViewerOutput* viewer_node() const; DecoderCache* decoder_cache(); @@ -109,6 +114,9 @@ private: qint64 cache_time_; QString cache_id_; +private slots: + void ThreadRequestedSibling(NodeDependency dep); + }; #endif // RENDERBACKEND_H diff --git a/app/render/backend/renderworker.cpp b/app/render/backend/renderworker.cpp index 19d600f3e..c24d86cfb 100644 --- a/app/render/backend/renderworker.cpp +++ b/app/render/backend/renderworker.cpp @@ -1,5 +1,7 @@ #include "renderworker.h" +#include + #include "node/block/block.h" RenderWorker::RenderWorker(DecoderCache *decoder_cache, QObject *parent) : @@ -50,6 +52,8 @@ void RenderWorker::Render(NodeDependency path) RenderInternal(path); + emit CompletedCache(path); + // Unlock all Nodes so changes can be made again foreach (Node* dep, all_nodes_in_graph) { dep->UnlockUserInput(); @@ -61,26 +65,47 @@ DecoderCache *RenderWorker::decoder_cache() return decoder_cache_; } -Node *RenderWorker::ValidateBlock(Node *n, const rational& time) +Block *RenderWorker::ValidateBlock(Block *block, const rational& time) { - if (n->IsBlock()) { - Block* block = static_cast(n); + Q_ASSERT(block != nullptr && time >= 0); - while (block != nullptr && block->in() > time) { - // This Block is too late, find an earlier one - block = block->previous(); - } - - while (block != nullptr && block->out() <= time) { - // This block is too early, find a later one - block = block->next(); - } - - // By this point, we should have the correct Block or nullptr if there's no Block here - return block; + while (block->in() > time) { + // This Block is too late, find an earlier one + block = block->previous(); } - return n; + while (block->out() <= time) { + // This block is too early, find a later one + if (block->next() == nullptr) { + break; + } + + block = block->next(); + } + + // By this point, we should have the correct Block or nullptr if there's no Block here + return block; +} + +QList RenderWorker::ValidateBlockRange(Block *n, const TimeRange &range) +{ + QList list; + Block* block_at_start = ValidateBlock(n, range.in()); + Block* block_at_end = ValidateBlock(n, range.out()); + + list.append(block_at_start); + + // If more than one block is active for this range + if (block_at_start != block_at_end) { + + // Collect all blocks between the start and the end + do { + block_at_start = block_at_start->next(); + list.append(block_at_start); + } while (block_at_start != block_at_end); + } + + return list; } void RenderWorker::RenderInternal(const NodeDependency &path) @@ -123,3 +148,128 @@ bool RenderWorker::IsStarted() { return started_; } + +QList RenderWorker::ProcessNodeInputsForTime(Node *n, const TimeRange &time) +{ + QList connected_inputs; + + // Now we need to gather information about this Node's inputs + foreach (NodeParam* param, n->parameters()) { + // Check if this parameter is an input and if the Node is dependent on it + if (param->type() == NodeParam::kInput) { + NodeInput* input = static_cast(param); + + if (input->dependent()) { + // If we're here, this input is necessary and we need to acquire the value for this Node + if (input->IsConnected()) { + // If it's connected to something, we need to retrieve that output at some point + connected_inputs.append(input); + } else { + // If it isn't connected, it'll have the value we need inside it. We just need to store it for the node. + input->set_stored_value(input->get_value_at_time(n->InputTimeAdjustment(input, time).in())); + } + + // Special types like FOOTAGE require extra work from us (to decrease node complexity dealing with decoders) + if (input->data_type() == NodeParam::kFootage) { + input->set_stored_value(0); + + DecoderPtr decoder = ResolveDecoderFromInput(input); + + // By this point we should definitely have a decoder, and if we don't something's gone terribly wrong + if (decoder != nullptr) { + FramePtr frame = decoder->Retrieve(time.in(), time.out() - time.in()); + + if (frame != nullptr) { + QVariant value = FrameToValue(frame); + + input->set_stored_value(value); + } + } + } + } + } + } + + return connected_inputs; +} + +QVariant RenderWorker::ProcessNodeNormally(const NodeDependency& dep) +{ + NodeOutput* output = dep.node(); + Node* node = dep.node()->parent(); + + //qDebug() << "Processing" << node->id(); + + // Check if the output already has a value for this time + if (output->has_cached_value(dep.range())) { + // If so, we don't need to do anything, we can just send this value and exit here + return output->get_cached_value(dep.range()); + } + + // We need to run the Node's code to get the correct value for this time + + QList connected_inputs = ProcessNodeInputsForTime(node, dep.range()); + + // For each connected input, we need to acquire the value from another node + while (!connected_inputs.isEmpty()) { + + // Remove any inputs from the list that we have valid cached values for already + for (int i=0;iget_connected_output(); + TimeRange input_time = node->InputTimeAdjustment(input, dep.range()); + + if (connected_output->has_cached_value(input_time)) { + // This output already has this value, no need to process it again + input->set_stored_value(connected_output->get_cached_value(input_time)); + connected_inputs.removeAt(i); + i--; + } + } + + // For every connected input except the first, we'll request another Node to do it + int input_for_this_thread = -1; + + for (int i=0;iget_connected_node()->IsProcessingLocked()) { + if (input_for_this_thread == -1) { + // Store this later since we can process it on this thread as we wait for other threads + input_for_this_thread = i; + } else { + TimeRange input_time = node->InputTimeAdjustment(input, dep.range()); + + emit RequestSibling(NodeDependency(input->get_connected_output(), + input_time)); + } + } + } + + if (input_for_this_thread > -1) { + // In the mean time, this thread can go off to do the first parameter + NodeInput* input = connected_inputs.at(input_for_this_thread); + TimeRange input_range = node->InputTimeAdjustment(input, dep.range()); + RenderAsSibling(NodeDependency(input->get_connected_output(), + input_range)); + input->set_stored_value(input->get_connected_output()->get_cached_value(input_range)); + connected_inputs.removeAt(input_for_this_thread); + } else { + // Nothing for this thread to do. We'll wait 0.5 sec and check again for other nodes + // FIXME: It would be nicer if this thread could do other nodes during this time + QThread::msleep(500); + } + } + + // By this point, the node should have all the inputs it needs to render correctly + + // Check if we have a shader for this output + if (OutputIsAccelerated(output)) { + // Run code + return RunNodeAccelerated(output); + } else { + // Generate the value as expected + return node->Value(output); + } +} diff --git a/app/render/backend/renderworker.h b/app/render/backend/renderworker.h index f2a30044b..134012363 100644 --- a/app/render/backend/renderworker.h +++ b/app/render/backend/renderworker.h @@ -3,6 +3,7 @@ #include +#include "node/block/block.h" #include "node/node.h" #include "decodercache.h" @@ -27,8 +28,36 @@ public slots: virtual void RenderAsSibling(NodeDependency dep) = 0; +signals: + void RequestSibling(NodeDependency path); + + void CompletedCache(NodeDependency dep); + protected: - Node* ValidateBlock(Node* n, const rational& time); + /** + * @brief Returns the block in a sequence that is active at a given time + * + * Blocks are connected to each other previous/next to create a BlockList or a sequence of blocks. The block that + * is currently "active" depends on the time and only one block in a track can be active at any given time. + * + * Calling this function with a block and a time will traverse the provided block's track to find the block that will + * be active at that time. The block must be valid (non-null) and the time must be valid (>= 0). + * + * This function may return the same block that it was called with. It will never return nullptr. + */ + Block *ValidateBlock(Block* block, const rational& time); + + /** + * @brief Returns all the blocks that could be active within a range of time + * + * Similar to ValidateBlock() but rather than returning one block for a single time, this function returns a list of + * blocks that could be active within a range of time. + * + * The block must be valid (non-null) and the time must be valid (>= 0). + * + * The list will always contain at least one entry. + */ + QList ValidateBlockRange(Block* n, const TimeRange& range); virtual bool InitInternal() = 0; @@ -36,11 +65,21 @@ protected: virtual void RenderInternal(const NodeDependency& path); + virtual bool OutputIsAccelerated(NodeOutput *output) = 0; + + virtual QVariant RunNodeAccelerated(NodeOutput *output) = 0; + StreamPtr ResolveStreamFromInput(NodeInput* input); DecoderPtr ResolveDecoderFromInput(NodeInput* input); QList ListNodeAndAllDependencies(Node* n); + QList ProcessNodeInputsForTime(Node* n, const TimeRange& time); + + virtual QVariant FrameToValue(FramePtr frame) = 0; + + QVariant ProcessNodeNormally(const NodeDependency &dep); + DecoderCache* decoder_cache(); QAtomicInt working_; diff --git a/app/render/backend/videorenderbackend.cpp b/app/render/backend/videorenderbackend.cpp index 253593074..905e1a970 100644 --- a/app/render/backend/videorenderbackend.cpp +++ b/app/render/backend/videorenderbackend.cpp @@ -36,11 +36,6 @@ VideoRenderBackend::VideoRenderBackend(QObject *parent) : SetCacheName("Test"); } -VideoRenderBackend::~VideoRenderBackend() -{ - Close(); -} - void VideoRenderBackend::InvalidateCache(const rational &start_range, const rational &end_range) { if (!params_.is_valid()) { @@ -51,10 +46,10 @@ void VideoRenderBackend::InvalidateCache(const rational &start_range, const rati rational start_range_adj = qMax(rational(0), start_range); rational end_range_adj = qMin(viewer_node()->Length(), end_range); - qDebug() << "Cache invalidated between" + /*qDebug() << "Cache invalidated between" << start_range_adj.toDouble() << "and" - << end_range_adj.toDouble(); + << end_range_adj.toDouble();*/ // Snap start_range to timebase double start_range_dbl = start_range_adj.toDouble(); @@ -100,6 +95,9 @@ void VideoRenderBackend::InvalidateCache(const rational &start_range, const rati } } + // Remove frames after this time code if it's changed + frame_cache_.Truncate(viewer_node()->Length()); + CacheNext(); } @@ -160,6 +158,14 @@ void VideoRenderBackend::CacheIDChangedEvent(const QString &id) frame_cache_.SetCacheID(id); } +void VideoRenderBackend::ConnectWorkerToThis(RenderWorker *processor) +{ + connect(processor, SIGNAL(CompletedFrame(NodeDependency, QByteArray)), this, SLOT(ThreadCompletedFrame(NodeDependency, QByteArray))); + connect(processor, SIGNAL(HashAlreadyBeingCached()), this, SLOT(ThreadSkippedFrame())); + connect(processor, SIGNAL(CompletedDownload(NodeDependency, QByteArray)), this, SLOT(ThreadCompletedDownload(NodeDependency, QByteArray))); + connect(processor, SIGNAL(HashAlreadyExists(NodeDependency, QByteArray)), this, SLOT(ThreadHashAlreadyExists(NodeDependency, QByteArray))); +} + VideoRenderFrameCache *VideoRenderBackend::frame_cache() { return &frame_cache_; @@ -207,3 +213,8 @@ const char *VideoRenderBackend::GetCachedFrame(const rational &time) return nullptr; } + +NodeInput *VideoRenderBackend::GetDependentInput() +{ + return viewer_node()->texture_input(); +} diff --git a/app/render/backend/videorenderbackend.h b/app/render/backend/videorenderbackend.h index 02c4e558c..5d61273e7 100644 --- a/app/render/backend/videorenderbackend.h +++ b/app/render/backend/videorenderbackend.h @@ -44,8 +44,6 @@ public: */ VideoRenderBackend(QObject* parent = nullptr); - virtual ~VideoRenderBackend() override; - /** * @brief Set parameters of the Renderer * @@ -77,6 +75,8 @@ protected: const char *GetCachedFrame(const rational& time); + virtual NodeInput* GetDependentInput() override; + VideoRenderFrameCache* frame_cache(); const VideoRenderingParams& params() const; @@ -88,6 +88,8 @@ protected: virtual void CacheIDChangedEvent(const QString& id) override; + virtual void ConnectWorkerToThis(RenderWorker* processor) override; + signals: void CachedFrameReady(const rational& time); diff --git a/app/render/backend/videorenderframecache.cpp b/app/render/backend/videorenderframecache.cpp index 5d6b24c1a..04ea96dd9 100644 --- a/app/render/backend/videorenderframecache.cpp +++ b/app/render/backend/videorenderframecache.cpp @@ -67,6 +67,19 @@ void VideoRenderFrameCache::RemoveHash(const rational &time) time_hash_map_.remove(time); } +void VideoRenderFrameCache::Truncate(const rational &time) +{ + QMap::iterator i = time_hash_map_.begin(); + + while (i != time_hash_map_.end()) { + if (i.key() >= time) { + i = time_hash_map_.erase(i); + } else { + i++; + } + } +} + void VideoRenderFrameCache::RemoveHashFromCurrentlyCaching(const QByteArray &hash) { currently_caching_lock_.lock(); diff --git a/app/render/backend/videorenderframecache.h b/app/render/backend/videorenderframecache.h index 4f6fbbda0..a96695632 100644 --- a/app/render/backend/videorenderframecache.h +++ b/app/render/backend/videorenderframecache.h @@ -37,6 +37,8 @@ public: void SetHash(const rational& time, const QByteArray& hash); void RemoveHash(const rational& time); + void Truncate(const rational& time); + private: void RemoveHashFromCurrentlyCaching(const QByteArray& hash); diff --git a/app/render/backend/videorenderworker.cpp b/app/render/backend/videorenderworker.cpp index 2df775198..e5fb3ddba 100644 --- a/app/render/backend/videorenderworker.cpp +++ b/app/render/backend/videorenderworker.cpp @@ -1,7 +1,5 @@ #include "videorenderworker.h" -#include - #include "common/define.h" #include "node/node.h" #include "render/pixelservice.h" @@ -43,8 +41,8 @@ void VideoRenderWorker::RenderInternal(const NodeDependency& path) void VideoRenderWorker::HashNodeRecursively(QCryptographicHash *hash, Node* n, const rational& time) { // Resolve BlockList - if (n->IsBlock() && (n = ValidateBlock(n, time)) == nullptr) { - return; + if (n->IsBlock()) { + n = ValidateBlock(static_cast(n), time); } // Add this Node's ID @@ -55,42 +53,44 @@ void VideoRenderWorker::HashNodeRecursively(QCryptographicHash *hash, Node* n, c if (param->type() == NodeParam::kInput) { NodeInput* input = static_cast(param); - // Get time adjustment - TimeRange range = n->InputTimeAdjustment(input, TimeRange(time, time)); + if (input->dependent()) { + // Get time adjustment + TimeRange range = n->InputTimeAdjustment(input, TimeRange(time, time)); - // For a single frame, we only care about one of the times - rational input_time = range.in(); + // For a single frame, we only care about one of the times + rational input_time = range.in(); - if (input->IsConnected()) { - // Traverse down this edge - HashNodeRecursively(hash, input->get_connected_node(), input_time); - } else { - // Grab the value at this time - QVariant value = input->get_value_at_time(input_time); - hash->addData(NodeParam::ValueToBytes(input->data_type(), value)); - } + if (input->IsConnected()) { + // Traverse down this edge + HashNodeRecursively(hash, input->get_connected_node(), input_time); + } else { + // Grab the value at this time + QVariant value = input->get_value_at_time(input_time); + hash->addData(NodeParam::ValueToBytes(input->data_type(), value)); + } - // 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); + // 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); - if (decoder != nullptr) { - // Add footage details to hash + if (decoder != nullptr) { + // Add footage details to hash - // Footage filename - hash->addData(stream->footage()->filename().toUtf8()); + // Footage filename + hash->addData(stream->footage()->filename().toUtf8()); - // Footage last modified date - hash->addData(stream->footage()->timestamp().toString().toUtf8()); + // Footage last modified date + hash->addData(stream->footage()->timestamp().toString().toUtf8()); - // Footage stream - hash->addData(QString::number(stream->index()).toUtf8()); + // Footage stream + hash->addData(QString::number(stream->index()).toUtf8()); - // Footage timestamp - hash->addData(QString::number(decoder->GetTimestampFromTime(time)).toUtf8()); + // Footage timestamp + hash->addData(QString::number(decoder->GetTimestampFromTime(time)).toUtf8()); - // FIXME: Add colorspace and alpha assoc + // FIXME: Add colorspace and alpha assoc + } } } } @@ -115,165 +115,40 @@ void VideoRenderWorker::CloseInternal() download_buffer_.clear(); } -QList VideoRenderWorker::ProcessNodeInputsForTime(Node *n, const TimeRange &time) -{ - QList connected_inputs; - - // Now we need to gather information about this Node's inputs - foreach (NodeParam* param, n->parameters()) { - // Check if this parameter is an input and if the Node is dependent on it - if (param->type() == NodeParam::kInput) { - NodeInput* input = static_cast(param); - - if (input->dependent()) { - // If we're here, this input is necessary and we need to acquire the value for this Node - if (input->IsConnected()) { - // If it's connected to something, we need to retrieve that output at some point - connected_inputs.append(input); - } else { - // If it isn't connected, it'll have the value we need inside it. We just need to store it for the node. - input->set_stored_value(input->get_value_at_time(n->InputTimeAdjustment(input, time).in())); - } - - // Special types like FOOTAGE require extra work from us (to decrease node complexity dealing with decoders) - if (input->data_type() == NodeParam::kFootage) { - input->set_stored_value(0); - - DecoderPtr decoder = ResolveDecoderFromInput(input); - - // By this point we should definitely have a decoder, and if we don't something's gone terribly wrong - if (decoder != nullptr) { - FramePtr frame = decoder->Retrieve(time.in()); - - if (frame != nullptr) { - QVariant value = FrameToTexture(frame); - - input->set_stored_value(value); - } - } - } - } - } - } - - return connected_inputs; -} - void VideoRenderWorker::RenderAsSibling(NodeDependency dep) { NodeOutput* output = dep.node(); Node* original_node = output->parent(); Node* node; rational time = dep.in(); - QList connected_inputs; QVariant value; // Set working state working_++; - qDebug() << "Processing" << original_node->id() << original_node; + //qDebug() << "Processing" << original_node->id() << original_node; original_node->LockProcessing(); // 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 = ValidateBlock(original_node, time)) == nullptr) { - // ValidateBlock() may have returned nullptr if there was no Block found at this time so no texture to return - output->cache_value(dep.range(), 0); + if (original_node->IsBlock()) { + node = ValidateBlock(static_cast(original_node), time); - original_node->UnlockProcessing(); - goto end_render; - } + if (original_node != node) { + // Ensure output is the output matching the node as it may have changed + output = static_cast(node->GetParameterWithID(output->id())); - if (original_node != node) { - // Ensure output is the output matching the node as it may have changed - output = static_cast(node->GetParameterWithID(output->id())); - - // Switch locks - original_node->UnlockProcessing(); - node->LockProcessing(); - - qDebug() << "Deftly switched from" << original_node->id() << original_node << "to" << node->id() << node; - } - - // Check if the output already has a value for this time - if (output->has_cached_value(dep.range())) { - // If so, we don't need to do anything, we can just send this value and exit here - dep.node()->cache_value(dep.range(), output->get_cached_value(dep.range())); - - qDebug() << "Found a cached value on" << node->id() << output->id() << "at" << dep.range().in() << "-" << dep.range().out(); - - node->UnlockProcessing(); - goto end_render; - } - - // We need to run the Node's code to get the correct value for this time - - connected_inputs = ProcessNodeInputsForTime(node, dep.range()); - - // For each connected input, we need to acquire the value from another node - while (!connected_inputs.isEmpty()) { - - // Remove any inputs from the list that we have valid cached values for already - for (int i=0;iget_connected_output(); - TimeRange input_time = node->InputTimeAdjustment(input, dep.range()); - - if (connected_output->has_cached_value(input_time)) { - // This output already has this value, no need to process it again - input->set_stored_value(connected_output->get_cached_value(input_time)); - connected_inputs.removeAt(i); - i--; - } + // Switch locks + original_node->UnlockProcessing(); + node->LockProcessing(); } - - // For every connected input except the first, we'll request another Node to do it - int input_for_this_thread = -1; - - for (int i=0;iget_connected_node()->IsProcessingLocked()) { - if (input_for_this_thread == -1) { - // Store this later since we can process it on this thread as we wait for other threads - input_for_this_thread = i; - } else { - TimeRange input_time = node->InputTimeAdjustment(input, dep.range()); - - emit RequestSibling(NodeDependency(input->get_connected_output(), - input_time)); - } - } - } - - if (input_for_this_thread > -1) { - // In the mean time, this thread can go off to do the first parameter - NodeInput* input = connected_inputs.at(input_for_this_thread); - TimeRange input_range = node->InputTimeAdjustment(input, dep.range()); - RenderAsSibling(NodeDependency(input->get_connected_output(), - input_range)); - input->set_stored_value(input->get_connected_output()->get_cached_value(input_range)); - connected_inputs.removeAt(input_for_this_thread); - } else { - // Nothing for this thread to do. We'll wait 0.5 sec and check again for other nodes - // FIXME: It would be nicer if this thread could do other nodes during this time - QThread::msleep(500); - } - } - - // By this point, the node should have all the inputs it needs to render correctly - - // Check if we have a shader for this output - if (OutputIsShader(output)) { - // Run code - value = RunNodeAsShader(output); } else { - // Generate the value as expected - value = node->Value(output); + node = original_node; } + value = ProcessNodeNormally(NodeDependency(output, dep.range())); + // Place the value into the output output->cache_value(dep.range(), value); dep.node()->cache_value(dep.range(), value); @@ -281,13 +156,10 @@ void VideoRenderWorker::RenderAsSibling(NodeDependency dep) // We're done! node->UnlockProcessing(); -end_render: // End this working state working_--; } - - void VideoRenderWorker::Download(NodeDependency dep, QByteArray hash, QVariant texture, QString filename) { working_++; @@ -305,7 +177,7 @@ void VideoRenderWorker::Download(NodeDependency dep, QByteArray hash, QVariant t std::unique_ptr out = OIIO::ImageOutput::create(working_fn_std); if (out) { - qDebug() << "Saving to" << filename; + //qDebug() << "Saving to" << filename; out->open(working_fn_std, spec); out->write_image(format_info.oiio_desc, download_buffer_.data()); out->close(); diff --git a/app/render/backend/videorenderworker.h b/app/render/backend/videorenderworker.h index 682c8bc4b..9f370ba52 100644 --- a/app/render/backend/videorenderworker.h +++ b/app/render/backend/videorenderworker.h @@ -21,8 +21,6 @@ public slots: void Download(NodeDependency dep, QByteArray hash, QVariant texture, QString filename); signals: - void RequestSibling(NodeDependency path); - void CompletedFrame(NodeDependency path, QByteArray hash); void CompletedDownload(NodeDependency path, QByteArray hash); @@ -36,16 +34,10 @@ protected: virtual void CloseInternal() override; - virtual QVariant FrameToTexture(FramePtr frame) = 0; - const VideoRenderingParams& video_params(); virtual void ParametersChangedEvent(){} - virtual bool OutputIsShader(NodeOutput *output) = 0; - - virtual QVariant RunNodeAsShader(NodeOutput *output) = 0; - virtual void TextureToBuffer(const QVariant& texture, QByteArray& buffer) = 0; virtual void RenderInternal(const NodeDependency& path) override; @@ -53,8 +45,6 @@ protected: private: void ProcessNode(); - QList ProcessNodeInputsForTime(Node* n, const TimeRange& time); - void HashNodeRecursively(QCryptographicHash* hash, Node *n, const rational &time); VideoRenderingParams video_params_; diff --git a/app/render/pixelservice.cpp b/app/render/pixelservice.cpp index 6ca6916ae..7a90843ba 100644 --- a/app/render/pixelservice.cpp +++ b/app/render/pixelservice.cpp @@ -122,7 +122,7 @@ FramePtr PixelService::ConvertPixelFormat(FramePtr frame, const olive::PixelForm switch (static_cast(frame->format())) { case olive::PIX_FMT_RGBA8: { - uint8_t* source = frame->data(); + uint8_t* source = reinterpret_cast(frame->data()); switch (dest_format) { case olive::PIX_FMT_RGBA16U: // 8-bit Integer -> 16-bit Integer @@ -163,7 +163,7 @@ FramePtr PixelService::ConvertPixelFormat(FramePtr frame, const olive::PixelForm switch (dest_format) { case olive::PIX_FMT_RGBA8: // 16-bit Integer -> 8-bit Integer { - uint8_t* destination = converted->data(); + uint8_t* destination = reinterpret_cast(converted->data()); for (int i=0;i(source[i] / 257); } @@ -199,7 +199,7 @@ FramePtr PixelService::ConvertPixelFormat(FramePtr frame, const olive::PixelForm switch (dest_format) { case olive::PIX_FMT_RGBA8: // 16-bit Float -> 8-bit Integer { - uint8_t* destination = converted->data(); + uint8_t* destination = reinterpret_cast(converted->data()); for (int i=0;i(source[i] * 255.0f); } @@ -235,7 +235,7 @@ FramePtr PixelService::ConvertPixelFormat(FramePtr frame, const olive::PixelForm switch (dest_format) { case olive::PIX_FMT_RGBA8: // 32-bit Float -> 8-bit Integer { - uint8_t* destination = converted->data(); + uint8_t* destination = reinterpret_cast(converted->data()); for (int i=0;i(source[i] * 255.0f); } @@ -293,7 +293,7 @@ void PixelService::ConvertRGBtoRGBA(FramePtr frame) while (rgb_iter >= 0) { memcpy(&frame->data()[rgba_iter], &frame->data()[rgb_iter], static_cast(rgb_pixel_size)); - uint8_t* alpha_ptr = &frame->data()[rgba_iter + rgb_pixel_size]; + uint8_t* alpha_ptr = reinterpret_cast(frame->data()) + rgba_iter + rgb_pixel_size; // Write a full alpha value according to the format switch (dest_format) {