ca33d895a3
## Summary of the Pull Request Moves the ConPTY drawing mechanism (`VtRenderer`) to use the fine-grained `til::bitmap` individual-dirty-bit tracking mechanism instead of coarse-grained rectangle unions to improve drawing performance by dramatically reducing the total area redrawn. ## PR Checklist * [x] Part of #778 and #1064 * [x] I work here * [x] Tests added and updated. * [x] I'm a core contributor ## Detailed Description of the Pull Request / Additional comments - Converted `GetDirtyArea()` interface from `IRenderEngine` to use a vector of `til::rectangle` instead of the `SMALL_RECT` to banhammer inclusive rectangles. - `VtEngine` now holds and operates on the `til::bitmap` for invalidation regions. All invalidation operation functions that used to be embedded inside `VtEngine` are deleted in favor of using the ones in `til::bitmap`. - Updated `VtEngine` tracing to use new `til::bitmap` on trace and the new `to_string()` methods detailed below. - Comparison operators for `til::bitmap` and complementary tests. - Fixed an issue where the dirty rectangle shortcut in `til::bitmap` was set to 0,0,0,0 by default which means that `|=` on it with each `set()` operation was stretching the rectangle from 0,0. Now it's a `std::optional` so it has no value after just being cleared and will build from whatever the first invalidated rectangle is. Complementary tests added. - Optional run caching for `til::bitmap` in the `runs()` method since both VT and DX renderers will likely want to generate the set of runs at the beginning of a frame and refer to them over and over through that frame. Saves the iteration and creation and caches inside `til::bitmap` where the chance of invalidation of the underlying data is known best. It is still possible to iterate manually with `begin()` and `end()` from the outside without caching, if desired. Complementary tests added. - WEX templates added for `til::bitmap` and used in tests. - `translate()` method for `til::bitmap` which will slide the dirty points in the direction specified by a `til::point` and optionally back-fill the uncovered area as dirty. Complementary tests added. - Moves all string generation for `til` types `size`, `point`, `rectangle`, and `some` into a `to_string` method on each object such that it can be used in both ETW tracing scenarios AND in the TAEF templates uniformly. Adds a similar method for `bitmap`. - Add tagging to `_bitmap_const_iterator` such that it appears as a valid **Input Iterator** to STL collections and can be used in a `std::vector` constructor as a range. Adds and cleans up operators on this iterator to match the theoretical requirements for an **Input Iterator**. Complementary tests added. - Add loose operators to `til` which will allow some basic math operations (+, -, *, /) between `til::size` and `til::point` and vice versa. Complementary tests added. Complementary tests added. - Adds operators to `til::rectangle` to allow scaling with basic math operations (+, -, *) versus `til::size` and translation with basic math operations (+, -) against `til::point`. Complementary tests added. - In-place variants of some operations added to assorted `til` objects. Complementary tests added. - Update VT tests to compare invalidation against the new map structure instead of raw rectangles where possible. ## Validation Steps Performed - Wrote additional til Unit Tests for all additional operators and functions added to the project to support this operation - Updated the existing VT renderer tests - Ran perf check
268 lines
6.5 KiB
C++
268 lines
6.5 KiB
C++
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
|
|
#pragma once
|
|
|
|
#include <array>
|
|
|
|
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|
{
|
|
template<class T, size_t N>
|
|
class some
|
|
{
|
|
private:
|
|
std::array<T, N> _array;
|
|
size_t _used;
|
|
|
|
#ifdef UNIT_TESTING
|
|
friend class SomeTests;
|
|
#endif
|
|
|
|
public:
|
|
using value_type = T;
|
|
using size_type = size_t;
|
|
using difference_type = ptrdiff_t;
|
|
using pointer = T*;
|
|
using const_pointer = const T*;
|
|
using reference = T&;
|
|
using const_reference = const T&;
|
|
|
|
using iterator = typename decltype(_array)::iterator;
|
|
using const_iterator = typename decltype(_array)::const_iterator;
|
|
|
|
using reverse_iterator = typename decltype(_array)::reverse_iterator;
|
|
using const_reverse_iterator = typename decltype(_array)::const_reverse_iterator;
|
|
|
|
some() noexcept :
|
|
_array{},
|
|
_used{ 0 }
|
|
{
|
|
}
|
|
|
|
some(std::initializer_list<T> init)
|
|
{
|
|
if (init.size() > N)
|
|
{
|
|
_invalidArg();
|
|
}
|
|
|
|
std::copy(init.begin(), init.end(), _array.begin());
|
|
_used = init.size();
|
|
}
|
|
|
|
constexpr bool operator==(const til::some<T, N>& other) const noexcept
|
|
{
|
|
return std::equal(cbegin(), cend(), other.cbegin(), other.cend());
|
|
}
|
|
|
|
constexpr bool operator!=(const til::some<T, N>& other) const noexcept
|
|
{
|
|
return !(*this == other);
|
|
}
|
|
|
|
void fill(const T& _Value)
|
|
{
|
|
_array.fill(_Value);
|
|
_used = N;
|
|
}
|
|
|
|
void swap(some& _Other) noexcept(std::is_nothrow_swappable<T>::value)
|
|
{
|
|
_array.swap(_Other._array);
|
|
std::swap(_used, _Other._used);
|
|
}
|
|
|
|
constexpr const_iterator begin() const noexcept
|
|
{
|
|
return _array.begin();
|
|
}
|
|
|
|
constexpr const_iterator end() const noexcept
|
|
{
|
|
return _array.begin() + _used;
|
|
}
|
|
|
|
constexpr const_reverse_iterator rbegin() const noexcept
|
|
{
|
|
return const_reverse_iterator(end());
|
|
}
|
|
|
|
constexpr const_reverse_iterator rend() const noexcept
|
|
{
|
|
return const_reverse_iterator(begin());
|
|
}
|
|
|
|
constexpr const_iterator cbegin() const noexcept
|
|
{
|
|
return begin();
|
|
}
|
|
|
|
constexpr const_iterator cend() const noexcept
|
|
{
|
|
return end();
|
|
}
|
|
|
|
constexpr const_reverse_iterator crbegin() const noexcept
|
|
{
|
|
return rbegin();
|
|
}
|
|
|
|
constexpr const_reverse_iterator crend() const noexcept
|
|
{
|
|
return rend();
|
|
}
|
|
|
|
constexpr size_type size() const noexcept
|
|
{
|
|
return _used;
|
|
}
|
|
|
|
constexpr size_type max_size() const noexcept
|
|
{
|
|
return N;
|
|
}
|
|
|
|
constexpr bool empty() const noexcept
|
|
{
|
|
return !_used;
|
|
}
|
|
|
|
constexpr void clear() noexcept
|
|
{
|
|
_used = 0;
|
|
_array = {}; // should free members, if necessary.
|
|
}
|
|
|
|
constexpr const_reference at(size_type pos) const
|
|
{
|
|
if (_used <= pos)
|
|
{
|
|
_outOfRange();
|
|
}
|
|
|
|
return _array[pos];
|
|
}
|
|
|
|
constexpr const_reference operator[](size_type pos) const noexcept
|
|
{
|
|
return _array[pos];
|
|
}
|
|
|
|
constexpr const_reference front() const noexcept
|
|
{
|
|
return _array[0];
|
|
}
|
|
|
|
constexpr const_reference back() const noexcept
|
|
{
|
|
return _array[_used - 1];
|
|
}
|
|
|
|
constexpr const T* data() const noexcept
|
|
{
|
|
return _array.data();
|
|
}
|
|
|
|
void push_back(const T& val)
|
|
{
|
|
if (_used >= N)
|
|
{
|
|
_outOfRange();
|
|
}
|
|
|
|
til::at(_array, _used) = val;
|
|
|
|
++_used;
|
|
}
|
|
|
|
void push_back(T&& val)
|
|
{
|
|
if (_used >= N)
|
|
{
|
|
_outOfRange();
|
|
}
|
|
|
|
til::at(_array, _used) = std::move(val);
|
|
|
|
++_used;
|
|
}
|
|
|
|
void pop_back()
|
|
{
|
|
if (_used <= 0)
|
|
{
|
|
_outOfRange();
|
|
}
|
|
|
|
--_used;
|
|
|
|
til::at(_array, _used) = 0;
|
|
}
|
|
|
|
[[noreturn]] void _invalidArg() const
|
|
{
|
|
throw std::invalid_argument("invalid argument");
|
|
}
|
|
|
|
[[noreturn]] void _outOfRange() const
|
|
{
|
|
throw std::out_of_range("invalid some<T, N> subscript");
|
|
}
|
|
|
|
std::wstring to_string() const
|
|
{
|
|
std::wstringstream wss;
|
|
wss << std::endl
|
|
<< L"Some contains " << size() << " of max size " << max_size() << ":" << std::endl;
|
|
wss << L"Elements:" << std::endl;
|
|
|
|
for (auto& item : *this)
|
|
{
|
|
wss << L"\t- " << item.to_string() << std::endl;
|
|
}
|
|
|
|
return wss.str();
|
|
}
|
|
};
|
|
}
|
|
|
|
#ifdef __WEX_COMMON_H__
|
|
namespace WEX::TestExecution
|
|
{
|
|
template<class T, size_t N>
|
|
class VerifyOutputTraits<::til::some<T, N>>
|
|
{
|
|
public:
|
|
static WEX::Common::NoThrowString ToString(const ::til::some<T, N>& some)
|
|
{
|
|
return WEX::Common::NoThrowString(some.to_string().c_str());
|
|
}
|
|
};
|
|
|
|
template<class T, size_t N>
|
|
class VerifyCompareTraits<::til::some<T, N>, ::til::some<T, N>>
|
|
{
|
|
public:
|
|
static bool AreEqual(const ::til::some<T, N>& expected, const ::til::some<T, N>& actual) noexcept
|
|
{
|
|
return expected == actual;
|
|
}
|
|
|
|
static bool AreSame(const ::til::some<T, N>& expected, const ::til::some<T, N>& actual) noexcept
|
|
{
|
|
return &expected == &actual;
|
|
}
|
|
|
|
static bool IsLessThan(const ::til::some<T, N>& expectedLess, const ::til::some<T, N>& expectedGreater) = delete;
|
|
|
|
static bool IsGreaterThan(const ::til::some<T, N>& expectedGreater, const ::til::some<T, N>& expectedLess) = delete;
|
|
|
|
static bool IsNull(const ::til::some<T, N>& object) noexcept
|
|
{
|
|
return object == til::some<T, N>{};
|
|
}
|
|
};
|
|
|
|
};
|
|
#endif
|