cache: add passthroughs to allow shallow copies of data
This commit is contained in:
@@ -183,7 +183,7 @@ rational ClipBlock::MediaToSequenceTime(const rational &media_time) const
|
||||
return sequence_time;
|
||||
}
|
||||
|
||||
void ClipBlock::RequestInvalidatedFromConnected(const TimeRange &range)
|
||||
void ClipBlock::RequestRangeFromConnected(const TimeRange &range)
|
||||
{
|
||||
Track::Type type = GetTrackType();
|
||||
|
||||
@@ -192,41 +192,75 @@ void ClipBlock::RequestInvalidatedFromConnected(const TimeRange &range)
|
||||
TimeRange max_range = InputTimeAdjustment(kBufferIn, -1, TimeRange(0, length()));
|
||||
if (type == Track::kVideo) {
|
||||
// Handle thumbnails
|
||||
RequestInvalidatedForCache(connected->thumbnail_cache(), max_range, range, true);
|
||||
RequestRangeForCache(connected->thumbnail_cache(), max_range, range, true, true);
|
||||
|
||||
// Handle video cache
|
||||
RequestInvalidatedForCache(connected->video_frame_cache(), max_range, range, IsAutocaching());
|
||||
RequestRangeForCache(connected->video_frame_cache(), max_range, range, true, IsAutocaching());
|
||||
} else if (type == Track::kAudio) {
|
||||
// Handle waveforms
|
||||
RequestInvalidatedForCache(connected->waveform_cache(), max_range, range, true);
|
||||
RequestRangeForCache(connected->waveform_cache(), max_range, range, true, true);
|
||||
|
||||
// Handle audio cache
|
||||
RequestInvalidatedForCache(connected->audio_playback_cache(), max_range, range, IsAutocaching());
|
||||
RequestRangeForCache(connected->audio_playback_cache(), max_range, range, true, IsAutocaching());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClipBlock::RequestInvalidatedForCache(PlaybackCache *cache, const TimeRange &max_range, const TimeRange &range, bool request)
|
||||
void ClipBlock::RequestInvalidatedFromConnected()
|
||||
{
|
||||
if ((range.in() == RATIONAL_MIN && range.out() == RATIONAL_MAX) || !range.length().isNull()) {
|
||||
// Request only this range
|
||||
TimeRange r = range.Intersected(max_range);
|
||||
cache->Invalidate(r);
|
||||
if (request) {
|
||||
emit cache->Request(r);
|
||||
}
|
||||
} else {
|
||||
// Request all ranges currently marked as invalid
|
||||
if (request) {
|
||||
TimeRangeList invalid = cache->GetInvalidatedRanges(max_range);
|
||||
for (const TimeRange &r : invalid) {
|
||||
emit cache->Request(r);
|
||||
Track::Type type = GetTrackType();
|
||||
|
||||
if (type == Track::kVideo || type == Track::kAudio) {
|
||||
if (Node *connected = GetConnectedOutput(kBufferIn)) {
|
||||
TimeRange max_range = InputTimeAdjustment(kBufferIn, -1, TimeRange(0, length()));
|
||||
if (type == Track::kVideo) {
|
||||
// Handle thumbnails
|
||||
RequestInvalidatedForCache(connected->thumbnail_cache(), max_range);
|
||||
|
||||
// Handle video cache
|
||||
if (IsAutocaching()) {
|
||||
RequestInvalidatedForCache(connected->video_frame_cache(), max_range);
|
||||
}
|
||||
} else if (type == Track::kAudio) {
|
||||
// Handle waveforms
|
||||
RequestInvalidatedForCache(connected->waveform_cache(), max_range);
|
||||
|
||||
// Handle audio cache
|
||||
if (IsAutocaching()) {
|
||||
RequestInvalidatedForCache(connected->audio_playback_cache(), max_range);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClipBlock::RequestRangeForCache(PlaybackCache *cache, const TimeRange &max_range, const TimeRange &range, bool invalidate, bool request)
|
||||
{
|
||||
TimeRange r = range.Intersected(max_range);
|
||||
|
||||
if (invalidate) {
|
||||
cache->Invalidate(r);
|
||||
}
|
||||
|
||||
if (request) {
|
||||
emit cache->Request(r);
|
||||
}
|
||||
}
|
||||
|
||||
void ClipBlock::RequestInvalidatedForCache(PlaybackCache *cache, const TimeRange &max_range)
|
||||
{
|
||||
TimeRangeList invalid = cache->GetInvalidatedRanges(max_range);
|
||||
|
||||
for (const PlaybackCache::Passthrough &p : cache->GetPassthroughs()) {
|
||||
invalid.remove(p);
|
||||
}
|
||||
|
||||
for (const TimeRange &r : invalid) {
|
||||
RequestRangeForCache(cache, max_range, r, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options)
|
||||
{
|
||||
Q_UNUSED(element)
|
||||
@@ -235,7 +269,7 @@ void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int
|
||||
if (from == kBufferIn) {
|
||||
// Render caches where necessary
|
||||
if (AreCachesEnabled()) {
|
||||
RequestInvalidatedFromConnected(range);
|
||||
RequestRangeFromConnected(range);
|
||||
}
|
||||
|
||||
// Adjust range from media time to sequence time
|
||||
@@ -380,6 +414,33 @@ void ClipBlock::Retranslate()
|
||||
SetInputName(kMaintainAudioPitchInput, tr("Maintain Audio Pitch"));
|
||||
}
|
||||
|
||||
void ClipBlock::AddCachePassthroughFrom(ClipBlock *other)
|
||||
{
|
||||
if (auto tc = this->video_frame_cache()) {
|
||||
if (auto oc = other->video_frame_cache()) {
|
||||
tc->SetPassthrough(oc);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto tc = this->audio_playback_cache()) {
|
||||
if (auto oc = other->audio_playback_cache()) {
|
||||
tc->SetPassthrough(oc);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto tc = this->thumbnails()) {
|
||||
if (auto oc = other->thumbnails()) {
|
||||
tc->SetPassthrough(oc);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto tc = this->waveform()) {
|
||||
if (auto oc = other->waveform()) {
|
||||
tc->SetPassthrough(oc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClipBlock::ConnectedToPreviewEvent()
|
||||
{
|
||||
RequestInvalidatedFromConnected();
|
||||
|
||||
+17
-10
@@ -122,7 +122,7 @@ public:
|
||||
return block_links_;
|
||||
}
|
||||
|
||||
const FrameHashCache *connected_video_cache() const
|
||||
FrameHashCache *connected_video_cache() const
|
||||
{
|
||||
if (Node *n = GetConnectedOutput(kBufferIn)) {
|
||||
return n->video_frame_cache();
|
||||
@@ -131,7 +131,16 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
const FrameHashCache *thumbnails()
|
||||
AudioPlaybackCache *connected_audio_cache() const
|
||||
{
|
||||
if (Node *n = GetConnectedOutput(kBufferIn)) {
|
||||
return n->audio_playback_cache();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
FrameHashCache *thumbnails()
|
||||
{
|
||||
if (Node *n = GetConnectedOutput(kBufferIn)) {
|
||||
return n->thumbnail_cache();
|
||||
@@ -140,7 +149,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
const AudioWaveformCache *waveform()
|
||||
AudioWaveformCache *waveform()
|
||||
{
|
||||
if (Node *n = GetConnectedOutput(kBufferIn)) {
|
||||
return n->waveform_cache();
|
||||
@@ -149,11 +158,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void set_waveform(const AudioVisualWaveform *w)
|
||||
{
|
||||
qDebug() << "WAVEFORM COPY STUB";
|
||||
//audio_playback_cache()->set_visual(w);
|
||||
}
|
||||
void AddCachePassthroughFrom(ClipBlock *other);
|
||||
|
||||
ViewerOutput *connected_viewer() const
|
||||
{
|
||||
@@ -194,9 +199,11 @@ private:
|
||||
|
||||
rational MediaToSequenceTime(const rational& media_time) const;
|
||||
|
||||
void RequestInvalidatedFromConnected(const TimeRange &range = TimeRange());
|
||||
void RequestRangeFromConnected(const TimeRange &range);
|
||||
void RequestInvalidatedFromConnected();
|
||||
|
||||
void RequestInvalidatedForCache(PlaybackCache *cache, const TimeRange &max_range, const TimeRange &range, bool request);
|
||||
void RequestRangeForCache(PlaybackCache *cache, const TimeRange &max_range, const TimeRange &range, bool invalidate, bool request);
|
||||
void RequestInvalidatedForCache(PlaybackCache *cache, const TimeRange &max_range);
|
||||
|
||||
QVector<Block*> block_links_;
|
||||
|
||||
|
||||
@@ -98,4 +98,15 @@ rational AudioWaveformCache::length() const
|
||||
return len;
|
||||
}
|
||||
|
||||
void AudioWaveformCache::SetPassthrough(PlaybackCache *cache)
|
||||
{
|
||||
AudioWaveformCache *c = static_cast<AudioWaveformCache*>(cache);
|
||||
waveforms_ = c->waveforms_;
|
||||
for (const TimeRange &r : c->GetValidatedRanges()) {
|
||||
Validate(r);
|
||||
}
|
||||
SetParameters(c->GetParameters());
|
||||
SetSavingEnabled(c->IsSavingEnabled());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ public:
|
||||
|
||||
rational length() const;
|
||||
|
||||
virtual void SetPassthrough(PlaybackCache *cache) override;
|
||||
|
||||
private:
|
||||
class TimeRangeWithWaveform : public TimeRange
|
||||
{
|
||||
|
||||
@@ -64,6 +64,21 @@ void FrameHashCache::ValidateTime(const rational &time)
|
||||
Validate(TimeRange(time, time + timebase_));
|
||||
}
|
||||
|
||||
QString FrameHashCache::GetValidCacheFilename(const rational &time) const
|
||||
{
|
||||
if (IsFrameCached(time)) {
|
||||
return CachePathName(time);
|
||||
} else if (!GetPassthroughs().empty()) {
|
||||
for (const Passthrough &p : GetPassthroughs()) {
|
||||
if (p.Contains(time)) {
|
||||
return CachePathName(GetCacheDirectory(), p.cache, time, timebase_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
bool FrameHashCache::SaveCacheFrame(const int64_t &time, FramePtr frame) const
|
||||
{
|
||||
return SaveCacheFrame(GetCacheDirectory(), GetUuid(), time, frame);
|
||||
@@ -224,6 +239,12 @@ FramePtr FrameHashCache::LoadCacheFrame(const QString &fn)
|
||||
return frame;
|
||||
}
|
||||
|
||||
void FrameHashCache::SetPassthrough(PlaybackCache *cache)
|
||||
{
|
||||
super::SetPassthrough(cache);
|
||||
SetTimebase(static_cast<FrameHashCache*>(cache)->GetTimebase());
|
||||
}
|
||||
|
||||
void FrameHashCache::LoadStateEvent(QDataStream &stream)
|
||||
{
|
||||
uint32_t version;
|
||||
|
||||
@@ -48,14 +48,7 @@ public:
|
||||
return GetValidatedRanges().contains(time);
|
||||
}
|
||||
|
||||
QString GetValidCacheFilename(const rational &time) const
|
||||
{
|
||||
if (IsFrameCached(time)) {
|
||||
return CachePathName(time);
|
||||
} else {
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
QString GetValidCacheFilename(const rational &time) const;
|
||||
|
||||
static bool SaveCacheFrame(const QString& filename, FramePtr frame);
|
||||
bool SaveCacheFrame(const int64_t &time, FramePtr frame) const;
|
||||
@@ -65,6 +58,8 @@ public:
|
||||
FramePtr LoadCacheFrame(const int64_t &time) const;
|
||||
static FramePtr LoadCacheFrame(const QString& fn);
|
||||
|
||||
virtual void SetPassthrough(PlaybackCache *cache) override;
|
||||
|
||||
protected:
|
||||
virtual void LoadStateEvent(QDataStream &stream) override;
|
||||
virtual void SaveStateEvent(QDataStream &stream) override;
|
||||
|
||||
@@ -36,6 +36,10 @@ void PlaybackCache::Invalidate(const TimeRange &r)
|
||||
|
||||
validated_.remove(r);
|
||||
|
||||
if (!passthroughs_.empty()) {
|
||||
TimeRangeList::util_remove(&passthroughs_, r);
|
||||
}
|
||||
|
||||
InvalidateEvent(r);
|
||||
|
||||
emit Invalidated(r);
|
||||
@@ -72,14 +76,14 @@ void PlaybackCache::LoadState()
|
||||
|
||||
LoadStateEvent(s);
|
||||
|
||||
int count;
|
||||
s >> count;
|
||||
|
||||
switch (version) {
|
||||
case 1:
|
||||
validated_.clear();
|
||||
{
|
||||
int valid_count, pass_count;
|
||||
|
||||
for (int i=0; i<count; i++) {
|
||||
validated_.clear();
|
||||
s >> valid_count;
|
||||
for (int i=0; i<valid_count; i++) {
|
||||
int in_num, in_den, out_num, out_den;
|
||||
|
||||
s >> in_num;
|
||||
@@ -89,8 +93,27 @@ void PlaybackCache::LoadState()
|
||||
|
||||
validated_.insert(TimeRange(rational(in_num, in_den), rational(out_num, out_den)));
|
||||
}
|
||||
|
||||
passthroughs_.clear();
|
||||
s >> pass_count;
|
||||
for (int i=0; i<pass_count; i++) {
|
||||
QUuid id;
|
||||
int in_num, in_den, out_num, out_den;
|
||||
|
||||
s >> in_num;
|
||||
s >> in_den;
|
||||
s >> out_num;
|
||||
s >> out_den;
|
||||
s >> id;
|
||||
|
||||
Passthrough p = TimeRange(rational(in_num, in_den), rational(out_num, out_den));
|
||||
p.cache = id;
|
||||
passthroughs_.append(p);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
f.close();
|
||||
}
|
||||
@@ -100,7 +123,7 @@ void PlaybackCache::SaveState()
|
||||
{
|
||||
QDir cache_dir = GetThisCacheDirectory();
|
||||
QFile f(cache_dir.filePath(QStringLiteral("state")));
|
||||
if (validated_.isEmpty()) {
|
||||
if (validated_.isEmpty() && passthroughs_.isEmpty()) {
|
||||
if (f.exists()) {
|
||||
f.remove();
|
||||
}
|
||||
@@ -123,6 +146,16 @@ void PlaybackCache::SaveState()
|
||||
s << r.out().denominator();
|
||||
}
|
||||
|
||||
s << passthroughs_.size();
|
||||
|
||||
for (const Passthrough &p : passthroughs_) {
|
||||
s << p.in().numerator();
|
||||
s << p.in().denominator();
|
||||
s << p.out().numerator();
|
||||
s << p.out().denominator();
|
||||
s << p.cache;
|
||||
}
|
||||
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
@@ -155,6 +188,21 @@ void PlaybackCache::Draw(QPainter *p, const rational &start, double scale, const
|
||||
}
|
||||
}
|
||||
|
||||
void PlaybackCache::SetPassthrough(PlaybackCache *cache)
|
||||
{
|
||||
for (const TimeRange &r : cache->GetValidatedRanges()) {
|
||||
Passthrough p = r;
|
||||
p.cache = cache->GetUuid();
|
||||
passthroughs_.push_back(p);
|
||||
}
|
||||
|
||||
passthroughs_.append(cache->GetPassthroughs());
|
||||
|
||||
if (saving_enabled_) {
|
||||
SaveState();
|
||||
}
|
||||
}
|
||||
|
||||
void PlaybackCache::InvalidateAll()
|
||||
{
|
||||
Invalidate(TimeRange(0, RATIONAL_MAX));
|
||||
@@ -211,6 +259,10 @@ TimeRangeList PlaybackCache::GetInvalidatedRanges(TimeRange intersecting) const
|
||||
invalidated.remove(range);
|
||||
}
|
||||
|
||||
foreach (const TimeRange &range, passthroughs_) {
|
||||
invalidated.remove(range);
|
||||
}
|
||||
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
|
||||
@@ -82,8 +82,22 @@ public:
|
||||
bool IsSavingEnabled() const { return saving_enabled_; }
|
||||
void SetSavingEnabled(bool e) { saving_enabled_ = e; }
|
||||
|
||||
virtual void SetPassthrough(PlaybackCache *cache);
|
||||
|
||||
QMutex *mutex() { return &mutex_; }
|
||||
|
||||
class Passthrough : public TimeRange
|
||||
{
|
||||
public:
|
||||
Passthrough(const TimeRange &r) :
|
||||
TimeRange(r)
|
||||
{}
|
||||
|
||||
QUuid cache;
|
||||
};
|
||||
|
||||
const QVector<Passthrough> &GetPassthroughs() const { return passthroughs_; }
|
||||
|
||||
public slots:
|
||||
void InvalidateAll();
|
||||
|
||||
@@ -116,6 +130,8 @@ private:
|
||||
|
||||
QMutex mutex_;
|
||||
|
||||
QVector<Passthrough> passthroughs_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -698,8 +698,7 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event)
|
||||
// Place the copy instead of the original block
|
||||
block = static_cast<Block*>(Node::CopyNodeInGraph(block, command));
|
||||
if (ClipBlock *new_clip = dynamic_cast<ClipBlock*>(block)) {
|
||||
qDebug() << "FIXME: Copy clip stub"; Q_UNUSED(new_clip)
|
||||
//new_clip->set_waveform(static_cast<ClipBlock*>(p.block)->waveform());
|
||||
new_clip->AddCachePassthroughFrom(static_cast<ClipBlock*>(p.block));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,27 +29,20 @@ namespace olive {
|
||||
//
|
||||
// BlockSplitCommand
|
||||
//
|
||||
void BlockSplitCommand::prepare()
|
||||
{
|
||||
reconnect_tree_command_ = new MultiUndoCommand();
|
||||
new_block_ = static_cast<Block*>(Node::CopyNodeInGraph(block_, reconnect_tree_command_));
|
||||
}
|
||||
|
||||
void BlockSplitCommand::redo()
|
||||
{
|
||||
old_length_ = block_->length();
|
||||
|
||||
Q_ASSERT(point_ > block_->in() && point_ < block_->out());
|
||||
|
||||
if (!reconnect_tree_command_) {
|
||||
reconnect_tree_command_ = new MultiUndoCommand();
|
||||
new_block_ = static_cast<Block*>(Node::CopyNodeInGraph(block_, reconnect_tree_command_));
|
||||
}
|
||||
|
||||
reconnect_tree_command_->redo_now();
|
||||
|
||||
if (ClipBlock *new_clip = dynamic_cast<ClipBlock*>(new_block_)) {
|
||||
ClipBlock *old_clip = static_cast<ClipBlock*>(block_);
|
||||
qDebug() << "FIXME: Copy waveform stub";
|
||||
Q_UNUSED(old_clip)
|
||||
Q_UNUSED(new_clip)
|
||||
//new_clip->set_waveform(old_clip->waveform());
|
||||
}
|
||||
|
||||
// Determine our new lengths
|
||||
rational new_length = point_ - block_->in();
|
||||
rational new_part_length = block_->out() - point_;
|
||||
@@ -64,6 +57,11 @@ void BlockSplitCommand::redo()
|
||||
// Insert new block
|
||||
track->InsertBlockAfter(new_block(), block_);
|
||||
|
||||
if (ClipBlock *new_clip = dynamic_cast<ClipBlock*>(new_block_)) {
|
||||
ClipBlock *old_clip = static_cast<ClipBlock*>(block_);
|
||||
new_clip->AddCachePassthroughFrom(old_clip);
|
||||
}
|
||||
|
||||
// If the block had an out transition, we move it to the new block
|
||||
moved_transition_ = NodeInput();
|
||||
|
||||
|
||||
@@ -54,6 +54,8 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void prepare() override;
|
||||
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
Reference in New Issue
Block a user