cache: use disk states to share caches
This commit is contained in:
@@ -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
|
||||
if (AreCachesEnabled()) {
|
||||
RequestInvalidatedFromConnected(range);
|
||||
}
|
||||
|
||||
// Adjust range from media time to sequence time
|
||||
TimeRange adj;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
+4
-2
@@ -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,6 +942,7 @@ void Node::InvalidateCache(const TimeRange &range, const QString &from, int elem
|
||||
Q_UNUSED(from)
|
||||
Q_UNUSED(element)
|
||||
|
||||
if (AreCachesEnabled()) {
|
||||
if (range.in() != range.out()) {
|
||||
TimeRange vr = range.Intersected(GetVideoCacheRange());
|
||||
if (vr.length() != 0) {
|
||||
@@ -954,6 +955,7 @@ void Node::InvalidateCache(const TimeRange &range, const QString &from, int elem
|
||||
waveform_cache()->Invalidate(ar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SendInvalidateCache(range, options);
|
||||
}
|
||||
|
||||
+5
-12
@@ -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<InputElementPair, ValueHint> 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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,8 +177,38 @@ FramePtr FrameHashCache::LoadCacheFrame(const QString &fn)
|
||||
}
|
||||
|
||||
file.setFrameBuffer(framebuffer);
|
||||
|
||||
file.readPixels(dw.min.y, dw.max.y);
|
||||
} catch (const std::exception &e) {
|
||||
// Not an EXR, maybe it's a JPEG?
|
||||
QImage img;
|
||||
|
||||
if (img.load(fn, "jpg")) {
|
||||
|
||||
// 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; i<img.height(); i++) {
|
||||
memcpy(frame->data() + 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
|
||||
@@ -187,6 +217,7 @@ FramePtr FrameHashCache::LoadCacheFrame(const QString &fn)
|
||||
// Assume this frame is corrupt in some way and delete it
|
||||
QMetaObject::invokeMethod(DiskManager::instance(), "DeleteSpecificFile", Q_ARG(QString, fn));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#define PLAYBACKCACHE_H
|
||||
|
||||
#include <QDir>
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QPainter>
|
||||
#include <QUuid>
|
||||
@@ -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_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user