diff --git a/app/node/color/opacity/opacity.cpp b/app/node/color/opacity/opacity.cpp index 4d89eb0b5..3d0084f7b 100644 --- a/app/node/color/opacity/opacity.cpp +++ b/app/node/color/opacity/opacity.cpp @@ -111,3 +111,13 @@ QVariant OpacityNode::Value(NodeOutput *output, const rational &time) return 0; } + +NodeInput *OpacityNode::texture_input() +{ + return texture_input_; +} + +NodeOutput *OpacityNode::texture_output() +{ + return texture_output_; +} diff --git a/app/node/color/opacity/opacity.h b/app/node/color/opacity/opacity.h index b90096b13..a604bc4ca 100644 --- a/app/node/color/opacity/opacity.h +++ b/app/node/color/opacity/opacity.h @@ -38,6 +38,10 @@ public: virtual QVariant Value(NodeOutput *output, const rational &time) override; + NodeInput* texture_input(); + + NodeOutput* texture_output(); + private: NodeInput* opacity_input_; diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index 33ae2b9e3..06cf6038f 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -21,7 +21,8 @@ #include "viewer.h" ViewerOutput::ViewerOutput() : - attached_viewer_(nullptr) + attached_viewer_(nullptr), + current_time_(0) { texture_input_ = new NodeInput("tex_out"); texture_input_->add_data_input(NodeInput::kTexture); diff --git a/app/node/processor/renderer/renderer.cpp b/app/node/processor/renderer/renderer.cpp index a3e1cb6c6..901f1d9cf 100644 --- a/app/node/processor/renderer/renderer.cpp +++ b/app/node/processor/renderer/renderer.cpp @@ -103,18 +103,26 @@ QVariant RendererProcessor::Value(NodeOutput* output, const rational& time) QString fn = CachePathName(time_hash_map_[time]); if (QFileInfo::exists(fn)) { + qDebug() << "Reading" << fn; auto in = OIIO::ImageInput::open(fn.toStdString()); if (in) { in->read_image(PixelService::GetPixelFormatInfo(format_).oiio_desc, cache_frame_load_buffer_.data()); in->close(); + qDebug() << "Stopped reading" << fn; master_texture_->Upload(cache_frame_load_buffer_.data()); return QVariant::fromValue(master_texture_); + } else { + qDebug() << "OIIO failed to read EXR:" << OIIO::geterror().c_str(); } + } else { + qDebug() << "Texture did NOT exist"; } + } else { + qDebug() << "Hash map did NOT have this time"; } } @@ -187,8 +195,6 @@ void RendererProcessor::InvalidateCache(const rational &start_range, const ratio cache_queue_.append(r); } } - - time_hash_map_.remove(r); } CacheNext(); @@ -270,7 +276,10 @@ void RendererProcessor::Start() download_threads_[i] = std::make_shared(ctx, effective_width_, effective_height_, format_, mode_); download_threads_[i]->StartThread(QThread::LowPriority); - connect(download_threads_[i].get(), SIGNAL(Downloaded(const rational&)), this, SLOT(DownloadThreadFinished(const rational&))); + connect(download_threads_[i].get(), + SIGNAL(Downloaded(const rational&, const QByteArray&)), + this, + SLOT(DownloadThreadFinished(const rational&, const QByteArray&))); } last_download_thread_ = 0; @@ -357,7 +366,13 @@ QString RendererProcessor::CachePathName(const QByteArray &hash) bool RendererProcessor::HasHash(const QByteArray &hash) { - return QFileInfo::exists(CachePathName(hash)); + download_list_mutex_.lock(); + + bool dl_list_contains = download_list_.contains(hash); + + download_list_mutex_.unlock(); + + return QFileInfo::exists(CachePathName(hash)) || dl_list_contains; } void RendererProcessor::CalculateEffectiveDimensions() @@ -374,17 +389,27 @@ void RendererProcessor::ThreadCallback() RenderTexturePtr texture = master_thread_->texture(); - if (texture != nullptr) { - QString fn = CachePathName(master_thread_->hash()); - download_threads_[last_download_thread_%download_threads_.size()]->Queue(texture, fn, cache_frame_); - last_download_thread_++; - } + bool hash_exists = HasHash(master_thread_->hash()); - time_hash_map_.insert(cache_frame_, master_thread_->hash()); + if (!hash_exists) { + if (texture == nullptr) { + // We didn't receive a texture to download, but the viewer may still need updating + DownloadThreadFinished(cache_frame_, master_thread_->hash()); + } else { + // We received a texture, time to start downloading it + download_list_mutex_.lock(); + download_list_.append(master_thread_->hash()); + download_list_mutex_.unlock(); - // We didn't receive a texture to download, but the viewer may still need updating - if (texture == nullptr) { - DownloadThreadFinished(cache_frame_); + QString fn = CachePathName(master_thread_->hash()); + + download_threads_[last_download_thread_%download_threads_.size()]->Queue(texture, + fn, + cache_frame_, + master_thread_->hash()); + + last_download_thread_++; + } } CacheNext(); @@ -395,16 +420,25 @@ void RendererProcessor::ThreadRequestSibling(NodeDependency dep) { // Try to queue another thread to run this dep in advance for (int i=0;iQueue(dep, false)) { + if (threads_.at(i).get() != master_thread_ + && threads_.at(i)->Queue(dep, false)) { return; } } } -void RendererProcessor::DownloadThreadFinished(const rational& time) +void RendererProcessor::DownloadThreadFinished(const rational& time, const QByteArray& hash) { - // Check if we just downloaded (akak finished caching) the frame we're currently on - if (texture_output_->IsConnected() && texture_output_->LastRequestedTime() == time) { + // Insert into hash map + time_hash_map_.insert(time, hash); + + download_list_mutex_.lock(); + download_list_.removeAll(hash); + download_list_mutex_.unlock(); + + // Check if we just downloaded (aka finished caching) the frame we're currently on + if (texture_output_->IsConnected() + && texture_output_->LastRequestedTime() == time) { texture_output_->ClearCachedValue(); Node::InvalidateCache(time, time, nullptr); diff --git a/app/node/processor/renderer/renderer.h b/app/node/processor/renderer/renderer.h index 1f3fb3d63..e351427ec 100644 --- a/app/node/processor/renderer/renderer.h +++ b/app/node/processor/renderer/renderer.h @@ -185,12 +185,15 @@ private: QMap time_hash_map_; + QMutex download_list_mutex_; + QVector download_list_; + private slots: void ThreadCallback(); void ThreadRequestSibling(NodeDependency dep); - void DownloadThreadFinished(const rational &time); + void DownloadThreadFinished(const rational &time, const QByteArray &hash); }; diff --git a/app/node/processor/renderer/rendererdownloadthread.cpp b/app/node/processor/renderer/rendererdownloadthread.cpp index 577ea3571..7b6798613 100644 --- a/app/node/processor/renderer/rendererdownloadthread.cpp +++ b/app/node/processor/renderer/rendererdownloadthread.cpp @@ -17,13 +17,11 @@ RendererDownloadThread::RendererDownloadThread(QOpenGLContext *share_ctx, { } -void RendererDownloadThread::Queue(RenderTexturePtr texture, const QString& fn, const rational& time) +void RendererDownloadThread::Queue(RenderTexturePtr texture, const QString& fn, const rational& time, const QByteArray &hash) { texture_queue_lock_.lock(); - texture_queue_.append(texture); - download_filenames_.append(fn); - texture_times_.append(time); + texture_queue_.append({texture, fn, time, hash}); wait_cond_.wakeAll(); @@ -48,9 +46,7 @@ void RendererDownloadThread::ProcessLoop() f->glGenFramebuffers(1, &read_buffer_); - RenderTexturePtr working_texture; - QString working_filename; - rational working_time; + DownloadQueueEntry entry; int buffer_size = PixelService::GetBufferSize(render_instance()->format(), render_instance()->width(), @@ -82,9 +78,7 @@ void RendererDownloadThread::ProcessLoop() break; } - working_texture = texture_queue_.takeFirst(); - working_filename = download_filenames_.takeFirst(); - working_time = texture_times_.takeFirst(); + entry = texture_queue_.takeFirst(); texture_queue_lock_.unlock(); @@ -95,13 +89,13 @@ void RendererDownloadThread::ProcessLoop() xf->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - working_texture->texture(), + entry.texture->texture(), 0); f->glReadPixels(0, 0, - working_texture->width(), - working_texture->height(), + entry.texture->width(), + entry.texture->height(), format_info.pixel_format, format_info.pixel_type, data_buffer.data()); @@ -114,18 +108,21 @@ void RendererDownloadThread::ProcessLoop() f->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - std::string working_fn_std = working_filename.toStdString(); + std::string working_fn_std = entry.filename.toStdString(); std::unique_ptr out = OIIO::ImageOutput::create(working_fn_std); if (out) { + qDebug() << "Writing" << entry.filename; out->open(working_fn_std, spec); out->write_image(format_info.oiio_desc, data_buffer.data()); out->close(); + qDebug() << "Stopped writing" << entry.filename; + + emit Downloaded(entry.time, entry.hash); + } else { + qWarning() << tr("Failed to open output file \"%1\"").arg(entry.filename); } - - emit Downloaded(working_time); - } f->glDeleteFramebuffers(1, &read_buffer_); diff --git a/app/node/processor/renderer/rendererdownloadthread.h b/app/node/processor/renderer/rendererdownloadthread.h index ebf27f1be..24a97cdc3 100644 --- a/app/node/processor/renderer/rendererdownloadthread.h +++ b/app/node/processor/renderer/rendererdownloadthread.h @@ -13,30 +13,35 @@ public: const olive::PixelFormat& format, const olive::RenderMode& mode); - void Queue(RenderTexturePtr texture, const QString &fn, const rational &time); + void Queue(RenderTexturePtr texture, const QString &fn, const rational &time, const QByteArray &hash); public slots: virtual void Cancel() override; signals: - void Downloaded(const rational& time); + void Downloaded(const rational& time, const QByteArray& hash); protected: virtual void ProcessLoop() override; private: + struct DownloadQueueEntry { + RenderTexturePtr texture; + QString filename; + rational time; + QByteArray hash; + }; + GLuint read_buffer_; - QVector texture_queue_; - - QVector download_filenames_; - - QVector texture_times_; + QVector texture_queue_; QMutex texture_queue_lock_; QAtomicInt cancelled_; + QByteArray hash_; + }; using RendererDownloadThreadPtr = std::shared_ptr; diff --git a/app/widget/timelineview/tool/import.cpp b/app/widget/timelineview/tool/import.cpp index c6750f8d8..08380c10f 100644 --- a/app/widget/timelineview/tool/import.cpp +++ b/app/widget/timelineview/tool/import.cpp @@ -25,6 +25,7 @@ #include "config/config.h" #include "common/qtversionabstraction.h" #include "node/distort/transform/transform.h" +#include "node/color/opacity/opacity.h" #include "node/input/media/media.h" TimelineView::ImportTool::ImportTool(TimelineView *parent) : @@ -157,16 +158,19 @@ void TimelineView::ImportTool::DragDrop(QDropEvent *event) ClipBlock* clip = new ClipBlock(); MediaInput* media = new MediaInput(); TransformDistort* transform = new TransformDistort(); + OpacityNode* opacity = new OpacityNode(); // Set parents to node_memory_manager in case no TimelineOutput receives this signal clip->setParent(&node_memory_manager); media->setParent(&node_memory_manager); transform->setParent(&node_memory_manager); + opacity->setParent(&node_memory_manager); clip->set_length(ghost->Length()); media->SetFootage(ghost->data(0).value()->footage()); - NodeParam::ConnectEdge(media->texture_output(), clip->texture_input()); + NodeParam::ConnectEdge(opacity->texture_output(), clip->texture_input()); + NodeParam::ConnectEdge(media->texture_output(), opacity->texture_input()); NodeParam::ConnectEdge(transform->matrix_output(), media->matrix_input()); if (event->keyboardModifiers() & Qt::ControlModifier) {