diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index 9d7044be5..28103720d 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -215,7 +215,9 @@ void ClipBlock::RequestInvalidatedForCache(PlaybackCache *cache, const TimeRange { if ((range.in() == RATIONAL_MIN && range.out() == RATIONAL_MAX) || !range.length().isNull()) { // Request only this range - emit cache->Request(range.Intersected(max_range)); + TimeRange r = range.Intersected(max_range); + cache->Invalidate(r); + emit cache->Request(r); } else { // Request all ranges currently marked as invalid TimeRangeList invalid = cache->GetInvalidatedRanges(max_range); @@ -232,7 +234,9 @@ void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int // If signal is from texture input, transform all times from media time to sequence time if (from == kBufferIn) { // Render caches where necessary - RequestInvalidatedFromConnected(range); + if (AreCachesEnabled()) { + RequestInvalidatedFromConnected(range); + } // Adjust range from media time to sequence time TimeRange adj; diff --git a/app/node/graph.cpp b/app/node/graph.cpp index b4cdf44bd..44165727f 100644 --- a/app/node/graph.cpp +++ b/app/node/graph.cpp @@ -39,6 +39,10 @@ void NodeGraph::Clear() { // By deleting the last nodes first, we assume that nodes that are most important are deleted last // (e.g. Project's ColorManager or ProjectSettingsNode. + for (auto it=node_children_.cbegin(); it!=node_children_.cend(); it++) { + (*it)->SetCachesEnabled(false); + } + while (!node_children_.isEmpty()) { delete node_children_.last(); } diff --git a/app/node/node.cpp b/app/node/node.cpp index 84f46ef96..7b2edc383 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -48,8 +48,8 @@ Node::Node() : can_be_deleted_(true), override_color_(-1), folder_(nullptr), - cache_result_(false), - flags_(kNone) + flags_(kNone), + caches_enabled_(true) { AddInput(kEnabledInput, NodeValue::kBoolean, true); @@ -942,16 +942,18 @@ void Node::InvalidateCache(const TimeRange &range, const QString &from, int elem Q_UNUSED(from) Q_UNUSED(element) - if (range.in() != range.out()) { - TimeRange vr = range.Intersected(GetVideoCacheRange()); - if (vr.length() != 0) { - video_frame_cache()->Invalidate(vr); - thumbnail_cache()->Invalidate(vr); - } - TimeRange ar = range.Intersected(GetAudioCacheRange()); - if (ar.length() != 0) { - audio_playback_cache()->Invalidate(ar); - waveform_cache()->Invalidate(ar); + if (AreCachesEnabled()) { + if (range.in() != range.out()) { + TimeRange vr = range.Intersected(GetVideoCacheRange()); + if (vr.length() != 0) { + video_frame_cache()->Invalidate(vr); + thumbnail_cache()->Invalidate(vr); + } + TimeRange ar = range.Intersected(GetAudioCacheRange()); + if (ar.length() != 0) { + audio_playback_cache()->Invalidate(ar); + waveform_cache()->Invalidate(ar); + } } } diff --git a/app/node/node.h b/app/node/node.h index d7a3292f9..2ca325d34 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -355,6 +355,9 @@ public: void CopyCacheUuidsFrom(Node *n); + bool AreCachesEnabled() const { return caches_enabled_; } + void SetCachesEnabled(bool e) { caches_enabled_ = e; } + virtual QString GetInputName(const QString& id) const; void SetInputName(const QString& id, const QString& name); @@ -930,16 +933,6 @@ public: folder_ = folder; } - bool GetCacheTextures() const - { - return cache_result_; - } - - void SetCacheTextures(bool e) - { - cache_result_ = e; - } - class ArrayRemoveCommand : public UndoCommand { public: @@ -1405,8 +1398,6 @@ private: Folder* folder_; - bool cache_result_; - QMap value_hints_; PositionMap context_positions_; @@ -1423,6 +1414,8 @@ private: AudioPlaybackCache *audio_cache_; AudioWaveformCache *waveform_cache_; + bool caches_enabled_; + private slots: /** * @brief Slot when a keyframe's time changes to keep the keyframes correctly sorted by time diff --git a/app/node/project/footage/footage.cpp b/app/node/project/footage/footage.cpp index d38b610c0..231431619 100644 --- a/app/node/project/footage/footage.cpp +++ b/app/node/project/footage/footage.cpp @@ -47,8 +47,6 @@ Footage::Footage(const QString &filename) : valid_(false), cancelled_(nullptr) { - SetCacheTextures(true); - PrependInput(kLoopModeInput, NodeValue::kCombo, 0, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); PrependInput(kFilenameInput, NodeValue::kFile, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); diff --git a/app/node/project/serializer/serializer220403.cpp b/app/node/project/serializer/serializer220403.cpp index a78e11b5b..54b1c60b0 100644 --- a/app/node/project/serializer/serializer220403.cpp +++ b/app/node/project/serializer/serializer220403.cpp @@ -90,7 +90,11 @@ ProjectSerializer220403::LoadData ProjectSerializer220403::Load(Project *project qWarning() << "Failed to find node with ID" << id; reader->skipCurrentElement(); } else { + // Disable cache while node is being loaded (we'll re-enable it later) + node->SetCachesEnabled(false); + LoadNode(node, xml_node_data, reader); + node->setParent(project); } } @@ -312,6 +316,11 @@ ProjectSerializer220403::LoadData ProjectSerializer220403::Load(Project *project } } + // Re-enable caches + for (Node *n : project->nodes()) { + n->SetCachesEnabled(true); + } + return load_data; } diff --git a/app/node/traverser.cpp b/app/node/traverser.cpp index c043d0636..568a7fa18 100644 --- a/app/node/traverser.cpp +++ b/app/node/traverser.cpp @@ -101,9 +101,12 @@ NodeValue NodeTraverser::GenerateRowValueElement(const Node *node, const QString NodeValue value = table->TakeAt(value_index); if (value.type() == NodeValue::kTexture) { + QMutexLocker locker(node->video_frame_cache()->mutex()); + + node->video_frame_cache()->LoadState(); + QString cache = node->video_frame_cache()->GetValidCacheFilename(time.in()); if (!cache.isEmpty()) { - qDebug() << "pushing cache job"; value.set_value(CacheJob(cache, value.data())); } } diff --git a/app/render/framehashcache.cpp b/app/render/framehashcache.cpp index decdbcc3d..adab73bfc 100644 --- a/app/render/framehashcache.cpp +++ b/app/render/framehashcache.cpp @@ -177,15 +177,46 @@ FramePtr FrameHashCache::LoadCacheFrame(const QString &fn) } file.setFrameBuffer(framebuffer); + file.readPixels(dw.min.y, dw.max.y); } catch (const std::exception &e) { - qCritical() << "Failed to read cache frame:" << e.what(); + // Not an EXR, maybe it's a JPEG? + QImage img; - // Clear frame to signal that nothing was loaded - frame = nullptr; + if (img.load(fn, "jpg")) { - // Assume this frame is corrupt in some way and delete it - QMetaObject::invokeMethod(DiskManager::instance(), "DeleteSpecificFile", Q_ARG(QString, fn)); + // FIXME: Hardcoded + const int div = 1; + const VideoParams::Format image_format = VideoParams::kFormatUnsigned8; + const int channel_count = 4; + const rational par(1, 1); + + frame = Frame::Create(); + frame->set_video_params(VideoParams(img.width() * div, + img.height() * div, + image_format, + channel_count, + par, + VideoParams::kInterlaceNone, + div)); + + frame->allocate(); + + for (int i=0; idata() + frame->linesize_bytes() * i, + img.bits() + img.bytesPerLine() * i, + frame->width() * frame->video_params().GetBytesPerPixel()); + } + + } else { + qCritical() << "Failed to read cache frame:" << e.what(); + + // Clear frame to signal that nothing was loaded + frame = nullptr; + + // Assume this frame is corrupt in some way and delete it + QMetaObject::invokeMethod(DiskManager::instance(), "DeleteSpecificFile", Q_ARG(QString, fn)); + } } } diff --git a/app/render/playbackcache.cpp b/app/render/playbackcache.cpp index fc28b7a21..5ccbf40a0 100644 --- a/app/render/playbackcache.cpp +++ b/app/render/playbackcache.cpp @@ -39,6 +39,8 @@ void PlaybackCache::Invalidate(const TimeRange &r) InvalidateEvent(r); emit Invalidated(r); + + SaveState(); } Node *PlaybackCache::parent() const @@ -89,8 +91,6 @@ void PlaybackCache::LoadState() } f.close(); - - f.close(); } } @@ -165,6 +165,8 @@ void PlaybackCache::Validate(const TimeRange &r, bool signal) if (signal) { emit Validated(r); } + + SaveState(); } void PlaybackCache::InvalidateEvent(const TimeRange &) @@ -182,6 +184,13 @@ PlaybackCache::PlaybackCache(QObject *parent) : uuid_ = QUuid::createUuid(); } +void PlaybackCache::SetUuid(const QUuid &u) +{ + uuid_ = u; + + LoadState(); +} + TimeRangeList PlaybackCache::GetInvalidatedRanges(TimeRange intersecting) const { TimeRangeList invalidated; diff --git a/app/render/playbackcache.h b/app/render/playbackcache.h index 2b49dc618..509db8dda 100644 --- a/app/render/playbackcache.h +++ b/app/render/playbackcache.h @@ -22,6 +22,7 @@ #define PLAYBACKCACHE_H #include +#include #include #include #include @@ -42,7 +43,7 @@ public: PlaybackCache(QObject* parent = nullptr); const QUuid &GetUuid() const { return uuid_; } - void SetUuid(const QUuid &u) { uuid_ = u; } + void SetUuid(const QUuid &u); TimeRangeList GetInvalidatedRanges(TimeRange intersecting) const; TimeRangeList GetInvalidatedRanges(const rational &length) const @@ -78,6 +79,8 @@ public: return QFontMetrics(QFont()).height()/4; } + QMutex *mutex() { return &mutex_; } + public slots: void InvalidateAll(); @@ -106,6 +109,8 @@ private: QUuid uuid_; + QMutex mutex_; + }; } diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index f3b4c23f9..b2cf4f4d2 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -269,6 +269,9 @@ void PreviewAutoCacher::AddNode(Node *node) // Add to project copy->setParent(&copied_project_); + // Disable caches for copy + copy->SetCachesEnabled(false); + // Copy cache UUIDs copy->CopyCacheUuidsFrom(node); @@ -380,10 +383,6 @@ void PreviewAutoCacher::ConnectToNodeCache(Node *node) &PlaybackCache::CancelAll, this, &PreviewAutoCacher::CancelForCache); - - node->video_frame_cache()->LoadState(); - node->audio_playback_cache()->LoadState(); - node->thumbnail_cache()->LoadState(); } void PreviewAutoCacher::DisconnectFromNodeCache(Node *node) @@ -417,10 +416,6 @@ void PreviewAutoCacher::DisconnectFromNodeCache(Node *node) &PlaybackCache::CancelAll, this, &PreviewAutoCacher::CancelForCache); - - node->video_frame_cache()->SaveState(); - node->audio_playback_cache()->SaveState(); - node->thumbnail_cache()->SaveState(); } void PreviewAutoCacher::UpdateGraphChangeValue() diff --git a/app/render/renderer.cpp b/app/render/renderer.cpp index 8ebdedb01..5bc61b653 100644 --- a/app/render/renderer.cpp +++ b/app/render/renderer.cpp @@ -74,6 +74,8 @@ TexturePtr Renderer::InterlaceTexture(TexturePtr top, TexturePtr bottom, const V QVariant Renderer::GetDefaultShader() { + QMutexLocker locker(&color_cache_mutex_); + if (default_shader_.isNull()) { default_shader_ = CreateNativeShader(ShaderCode(QString(), QString())); } diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 4402042d3..75ee420d2 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -253,21 +253,25 @@ DecoderPtr RenderProcessor::ResolveDecoderFromInput(const QString& decoder_id, c qint64 file_last_modified = QFileInfo(stream.filename()).lastModified().toMSecsSinceEpoch(); - if (!decoder.decoder || decoder.last_modified != file_last_modified) { + DecoderPtr dec = nullptr; + + if (decoder.decoder && decoder.last_modified == file_last_modified) { + dec = decoder.decoder; + } else { // No decoder - decoder.decoder = Decoder::CreateFromID(decoder_id); + decoder.decoder = dec = Decoder::CreateFromID(decoder_id); decoder.last_modified = file_last_modified; decoder_cache_->insert(stream, decoder); locker.unlock(); - if (!decoder.decoder->Open(stream)) { + if (!dec->Open(stream)) { qWarning() << "Failed to open decoder for" << stream.filename() << "::" << stream.stream(); return nullptr; } } - return decoder.decoder; + return dec; } void RenderProcessor::Process(RenderTicketPtr ticket, Renderer *render_ctx, DecoderCache *decoder_cache, ShaderCache *shader_cache) diff --git a/app/widget/timeruler/timeruler.cpp b/app/widget/timeruler/timeruler.cpp index 59a234176..9e42c688b 100644 --- a/app/widget/timeruler/timeruler.cpp +++ b/app/widget/timeruler/timeruler.cpp @@ -252,7 +252,7 @@ void TimeRuler::drawForeground(QPainter *p, const QRectF &rect) } // If cache status is enabled - if (show_cache_status_ && playback_cache_) { + if (show_cache_status_ && playback_cache_ && playback_cache_->HasValidatedRanges()) { // FIXME: Hardcoded to get video length, if we ever need audio length, this will have to change int h = PlaybackCache::GetCacheIndicatorHeight(); QRect cache_rect(0, height() - h, width(), h);