Files
renderdoc/renderdoc/core/intervals.h
T

289 lines
8.9 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019-2020 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#pragma once
#include <map>
#include "api/replay/rdcflatmap.h"
#include "api/replay/renderdoc_replay.h"
#include "common/common.h"
template <typename T>
struct Intervals;
template <typename T, typename Map, typename Iter, typename Interval>
class IntervalsIter;
// An interval in an `Intervals<T>` instance.
template <typename T, typename Map, typename Iter>
class ConstIntervalRef
{
friend class IntervalsIter<T, Map, Iter, ConstIntervalRef>;
protected:
Iter iter;
Map *owner;
ConstIntervalRef(Map *owner, Iter iter) : iter(iter), owner(owner) {}
public:
// Inclusive lower bound
inline uint64_t start() const { return iter->first; }
// Exclusive upper bound
inline uint64_t finish() const
{
Iter next = iter;
next++;
if(next == owner->end())
{
return UINT64_MAX;
}
return next->first;
}
// Value associated with this interval
inline const T &value() const { return iter->second; }
};
// A mutable interval in an `Intervals<T>` instance
template <typename T, typename Map, typename Iter>
class IntervalRef : public ConstIntervalRef<T, Map, Iter>
{
friend class IntervalsIter<T, Map, Iter, IntervalRef>;
protected:
IntervalRef(Map *owner, Iter iter) : ConstIntervalRef<T, Map, Iter>(owner, iter) {}
public:
inline void setValue(const T &val) { this->iter->second = val; }
// Split this interval into two intervals:
// [start, x), [x, finish)
// This iterator will point to [x, finish) after the split.
// `x` must be in the interval [start, finish).
// If `x == start`, then `split(x)` is a no-op.
inline void split(uint64_t x)
{
if(this->start() < x)
this->iter = this->owner->insert(rdcpair<uint64_t, T>(x, this->value())).first;
}
// Merge this interval with the interval to the left, if both intervals have
// the same value.
// This iterator will point to the merged interval, if the merge is actually
// performed; otherwise this iterator is unmodified.
inline void mergeLeft()
{
if(this->iter != this->owner->begin())
{
auto prev_it = this->iter;
prev_it--;
if(this->iter->second == prev_it->second)
{
this->owner->erase(this->iter);
this->iter = prev_it;
}
}
}
};
// An iterator in an `Intervals<T>` instance.
template <typename T, typename Map, typename Iter, typename Interval>
class IntervalsIter
{
friend struct Intervals<T>;
protected:
Interval ref;
IntervalsIter(Map *owner, Iter iter) : ref(owner, iter) {}
Iter unwrap() { return ref.iter; }
public:
IntervalsIter(const IntervalsIter &src) : ref(src.ref) {}
IntervalsIter &operator++()
{
++ref.iter;
return *this;
}
IntervalsIter operator++(int)
{
IntervalsIter tmp(*this);
operator++();
return tmp;
}
IntervalsIter &operator--()
{
--ref.iter;
return *this;
}
IntervalsIter operator--(int)
{
IntervalsIter tmp(*this);
operator--();
return tmp;
}
bool operator==(const IntervalsIter &rhs) const
{
return ref.iter == rhs.ref.iter && ref.owner == rhs.ref.owner;
}
bool operator!=(const IntervalsIter &rhs) const
{
return ref.iter != rhs.ref.iter && ref.owner == rhs.ref.owner;
}
IntervalsIter &operator=(const IntervalsIter &rhs)
{
ref.iter = rhs.ref.iter;
ref.owner = rhs.ref.owner;
return *this;
}
inline Interval *operator->() { return &ref; }
};
// Data structure to efficiently store values for disjoint intervals.
template <typename T>
struct Intervals
{
public:
using MapType = rdcflatmap<uint64_t, T, 0>;
typedef IntervalRef<T, MapType, typename MapType::iterator> interval;
typedef IntervalsIter<T, MapType, typename MapType::iterator, interval> iterator;
typedef ConstIntervalRef<T, const MapType, typename MapType::const_iterator> const_interval;
typedef IntervalsIter<T, const MapType, typename MapType::const_iterator, const_interval> const_iterator;
private:
MapType StartPoints;
iterator Wrap(typename MapType::iterator iter) { return iterator(&StartPoints, iter); }
const_iterator Wrap(typename MapType::const_iterator iter) const
{
return const_iterator(&StartPoints, iter);
}
public:
Intervals() { StartPoints.insert({0, T()}); }
inline iterator end() { return Wrap(StartPoints.end()); }
inline iterator begin() { return Wrap(StartPoints.begin()); }
inline const_iterator begin() const { return Wrap(StartPoints.begin()); }
inline const_iterator end() const { return Wrap(StartPoints.end()); }
typedef typename MapType::size_type size_type;
inline size_type size() const { return StartPoints.size(); }
// Find the interval containing `x`.
iterator find(uint64_t x)
{
// Find the first interval starting after `x`; return the preceding interval.
auto it = StartPoints.upper_bound(x);
it--;
return Wrap(it);
}
// Find the interval containing `x`.
const_iterator find(uint64_t x) const
{
// Find the first interval starting after `x`; return the preceding interval.
auto it = StartPoints.upper_bound(x);
it--;
return Wrap(it);
}
// Update the values of overlapping intervals to `comp(oldValue, val)`
// (where `oldValue` is the value of the interval prior to calling `update`).
// If start/finish do not lie on the boundaries between intervals, the intervals
// will be split as necessary.
template <typename Compose>
void update(uint64_t start, uint64_t finish, T val, Compose comp)
{
if(finish <= start)
return;
auto i = find(start);
// Split the interval so that `i.start == start`
i->split(start);
// Loop over all the intervals in `a` that intersect the interval [start, finish)
for(; i != end() && i->start() < finish; i++)
{
if(i->finish() > finish)
{
// In this case, interval `i` extends beyond `finish`;
// split `i` so that we only update the portion of `i` in [start, finish).
i->split(finish);
// `split` leaves `i` pointing at the interval starting at `finish`;
// move back to the interval finishing at `finish`.
i--;
}
i->setValue(comp(i->value(), val));
i->mergeLeft();
}
// `i` now points to the interval following the last interval whose value was
// modified; merge `i` with that last modified interval, if the values match.
if(i != end())
i->mergeLeft();
}
// Update `this` by composing the value of each interval with the value of the
// corresponding interval in `other`.
// If the intervals in `this` and `other` do not line up, then the intervals in
// `this` will be split as necessary.
template <typename Compose>
void merge(const Intervals &other, Compose comp)
{
auto j = other.begin();
auto i = begin();
// Loop over the intervals in `this` (iterator `i`), while maintaining the
// interval `j` in `other` that contains `i`.
// The intervals in `this` are split as necessary, so that each `i` is
// contained in a single interval of `other`.
// Loop invariants:
// * i.start() >= j.start()
// * i.start() < j.end()
while(true)
{
if(i->finish() > j->finish())
{
i->split(j->finish());
i--;
}
// Now i is contained in j, so we can update the value of all of i
i->setValue(comp(i->value(), j->value()));
// The value of i and the interval left of i are now final;
// if these two intervals now have the same value, they can safely be
// merged into a single interval.
i->mergeLeft();
// Move to the next interval in `this`; also advance to the next interval
// in `other`, if necessary to maintain the invariant `i.start < j.end`.
i++;
if(i == end())
return;
if(i->start() >= j->finish())
j++;
}
}
};