mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-12 13:00:32 +00:00
289 lines
8.9 KiB
C++
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++;
|
|
}
|
|
}
|
|
};
|