[Impeller] Add utility for type safe masks. (flutter/engine#51361)

We use and recommend type safe enums. But when it comes to masks of those enums (`TextureUsageMask`, `ColorWriteMask`, etc.), these are all untyped and you can create one either from a scalar of the enums underlying type, or using ugly and error prone static casts. At the callee, there is no type checking and another error prone static cast.

This patch adds a type-safe mask type. This should allow for the migration of something like:

```c++
using TextureUsageMask = uint64_t;
```

with

```c++
using TextureUsageMask = Mask<TextureUsage>;
```

Subsequently, all static casts can be removed with full type checking throughout.

This doesn't migrate existing uses yet. That will come in a separate patch.
This commit is contained in:
Chinmay Garde
2024-03-12 13:59:08 -07:00
committed by GitHub
parent fde1e9cf6c
commit 67ee7c76fe
4 changed files with 392 additions and 0 deletions

View File

@@ -39503,6 +39503,7 @@ ORIGIN: ../../../flutter/impeller/base/backend_cast.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/base/comparable.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/base/comparable.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/base/config.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/base/mask.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/base/promise.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/base/promise.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/base/strings.cc + ../../../flutter/LICENSE
@@ -42354,6 +42355,7 @@ FILE: ../../../flutter/impeller/base/backend_cast.h
FILE: ../../../flutter/impeller/base/comparable.cc
FILE: ../../../flutter/impeller/base/comparable.h
FILE: ../../../flutter/impeller/base/config.h
FILE: ../../../flutter/impeller/base/mask.h
FILE: ../../../flutter/impeller/base/promise.cc
FILE: ../../../flutter/impeller/base/promise.h
FILE: ../../../flutter/impeller/base/strings.cc

View File

@@ -12,6 +12,7 @@ impeller_component("base") {
"comparable.cc",
"comparable.h",
"config.h",
"mask.h",
"promise.cc",
"promise.h",
"strings.cc",

View File

@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "flutter/testing/testing.h"
#include "impeller/base/mask.h"
#include "impeller/base/promise.h"
#include "impeller/base/strings.h"
#include "impeller/base/thread.h"
@@ -250,5 +251,198 @@ TEST(BaseTest, NoExceptionPromiseEmpty) {
wrapper.reset();
}
enum class MyMaskBits : uint32_t {
kFoo = 0,
kBar = 1 << 0,
kBaz = 1 << 1,
kBang = 1 << 2,
};
using MyMask = Mask<MyMaskBits>;
TEST(BaseTest, CanUseTypedMasks) {
{
MyMask mask;
ASSERT_EQ(static_cast<uint32_t>(mask), 0u);
ASSERT_FALSE(mask);
}
{
MyMask mask(MyMaskBits::kBar);
ASSERT_EQ(static_cast<uint32_t>(mask), 1u);
ASSERT_TRUE(mask);
}
{
MyMask mask2(MyMaskBits::kBar);
MyMask mask(mask2);
ASSERT_EQ(static_cast<uint32_t>(mask), 1u);
ASSERT_TRUE(mask);
}
{
MyMask mask2(MyMaskBits::kBar);
MyMask mask(std::move(mask2)); // NOLINT
ASSERT_EQ(static_cast<uint32_t>(mask), 1u);
ASSERT_TRUE(mask);
}
ASSERT_LT(MyMaskBits::kBar, MyMaskBits::kBaz);
ASSERT_LE(MyMaskBits::kBar, MyMaskBits::kBaz);
ASSERT_GT(MyMaskBits::kBaz, MyMaskBits::kBar);
ASSERT_GE(MyMaskBits::kBaz, MyMaskBits::kBar);
ASSERT_EQ(MyMaskBits::kBaz, MyMaskBits::kBaz);
ASSERT_NE(MyMaskBits::kBaz, MyMaskBits::kBang);
{
MyMask m1(MyMaskBits::kBar);
MyMask m2(MyMaskBits::kBaz);
ASSERT_EQ(static_cast<uint32_t>(m1 & m2), 0u);
ASSERT_FALSE(m1 & m2);
}
{
MyMask m1(MyMaskBits::kBar);
MyMask m2(MyMaskBits::kBaz);
ASSERT_EQ(static_cast<uint32_t>(m1 | m2), ((1u << 0u) | (1u << 1u)));
ASSERT_TRUE(m1 | m2);
}
{
MyMask m1(MyMaskBits::kBar);
MyMask m2(MyMaskBits::kBaz);
ASSERT_EQ(static_cast<uint32_t>(m1 ^ m2), ((1u << 0u) ^ (1u << 1u)));
ASSERT_TRUE(m1 ^ m2);
}
{
MyMask m1(MyMaskBits::kBar);
ASSERT_EQ(static_cast<uint32_t>(~m1), (~(1u << 0u)));
ASSERT_TRUE(m1);
}
{
MyMask m1 = MyMaskBits::kBar;
MyMask m2 = MyMaskBits::kBaz;
m2 = m1;
ASSERT_EQ(m2, MyMaskBits::kBar);
}
{
MyMask m = MyMaskBits::kBar | MyMaskBits::kBaz;
ASSERT_TRUE(m);
}
{
MyMask m = MyMaskBits::kBar & MyMaskBits::kBaz;
ASSERT_FALSE(m);
}
{
MyMask m = MyMaskBits::kBar ^ MyMaskBits::kBaz;
ASSERT_TRUE(m);
}
{
MyMask m = ~MyMaskBits::kBar;
ASSERT_TRUE(m);
}
{
MyMask m1 = MyMaskBits::kBar;
MyMask m2 = MyMaskBits::kBaz;
m2 |= m1;
ASSERT_EQ(m1, MyMaskBits::kBar);
MyMask pred = MyMaskBits::kBar | MyMaskBits::kBaz;
ASSERT_EQ(m2, pred);
}
{
MyMask m1 = MyMaskBits::kBar;
MyMask m2 = MyMaskBits::kBaz;
m2 &= m1;
ASSERT_EQ(m1, MyMaskBits::kBar);
MyMask pred = MyMaskBits::kBar & MyMaskBits::kBaz;
ASSERT_EQ(m2, pred);
}
{
MyMask m1 = MyMaskBits::kBar;
MyMask m2 = MyMaskBits::kBaz;
m2 ^= m1;
ASSERT_EQ(m1, MyMaskBits::kBar);
MyMask pred = MyMaskBits::kBar ^ MyMaskBits::kBaz;
ASSERT_EQ(m2, pred);
}
{
MyMask x = MyMaskBits::kBar;
MyMask m = x | MyMaskBits::kBaz;
ASSERT_TRUE(m);
}
{
MyMask x = MyMaskBits::kBar;
MyMask m = MyMaskBits::kBaz | x;
ASSERT_TRUE(m);
}
{
MyMask x = MyMaskBits::kBar;
MyMask m = x & MyMaskBits::kBaz;
ASSERT_FALSE(m);
}
{
MyMask x = MyMaskBits::kBar;
MyMask m = MyMaskBits::kBaz & x;
ASSERT_FALSE(m);
}
{
MyMask x = MyMaskBits::kBar;
MyMask m = x ^ MyMaskBits::kBaz;
ASSERT_TRUE(m);
}
{
MyMask x = MyMaskBits::kBar;
MyMask m = MyMaskBits::kBaz ^ x;
ASSERT_TRUE(m);
}
{
MyMask x = MyMaskBits::kBar;
MyMask m = ~x;
ASSERT_TRUE(m);
}
{
MyMaskBits x = MyMaskBits::kBar;
MyMask m = MyMaskBits::kBaz;
ASSERT_TRUE(x < m);
ASSERT_TRUE(x <= m);
}
{
MyMaskBits x = MyMaskBits::kBar;
MyMask m = MyMaskBits::kBaz;
ASSERT_FALSE(x == m);
}
{
MyMaskBits x = MyMaskBits::kBar;
MyMask m = MyMaskBits::kBaz;
ASSERT_TRUE(x != m);
}
{
MyMaskBits x = MyMaskBits::kBar;
MyMask m = MyMaskBits::kBaz;
ASSERT_FALSE(x > m);
ASSERT_FALSE(x >= m);
}
}
} // namespace testing
} // namespace impeller

View File

@@ -0,0 +1,195 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_IMPELLER_BASE_MASK_H_
#define FLUTTER_IMPELLER_BASE_MASK_H_
#include <type_traits>
namespace impeller {
//------------------------------------------------------------------------------
/// @brief A mask of typed enums.
///
/// @tparam EnumType_ The type of the enum. Must be an enum class.
///
template <typename EnumType_>
struct Mask {
using EnumType = EnumType_;
using MaskType = typename std::underlying_type<EnumType>::type;
constexpr Mask() = default;
constexpr Mask(const Mask<EnumType>& other) = default;
constexpr Mask(Mask<EnumType>&& other) = default;
constexpr Mask(EnumType type) // NOLINT(google-explicit-constructor)
: mask_(static_cast<MaskType>(type)) {}
explicit constexpr Mask(MaskType mask) : mask_(static_cast<MaskType>(mask)) {}
// All casts must be explicit.
explicit constexpr operator MaskType() const { return mask_; }
explicit constexpr operator bool() const { return !!mask_; }
// The following relational operators can be replaced with a defaulted
// spaceship operator post C++20.
constexpr bool operator<(const Mask<EnumType>& other) const {
return mask_ < other.mask_;
}
constexpr bool operator>(const Mask<EnumType>& other) const {
return mask_ > other.mask_;
}
constexpr bool operator>=(const Mask<EnumType>& other) const {
return mask_ >= other.mask_;
}
constexpr bool operator<=(const Mask<EnumType>& other) const {
return mask_ <= other.mask_;
}
constexpr bool operator==(const Mask<EnumType>& other) const {
return mask_ == other.mask_;
}
constexpr bool operator!=(const Mask<EnumType>& other) const {
return mask_ != other.mask_;
}
// Logical operators.
constexpr bool operator!() const { return !mask_; }
// Bitwise operators.
constexpr Mask<EnumType> operator&(const Mask<EnumType>& other) const {
return Mask<EnumType>{mask_ & other.mask_};
}
constexpr Mask<EnumType> operator|(const Mask<EnumType>& other) const {
return Mask<EnumType>{mask_ | other.mask_};
}
constexpr Mask<EnumType> operator^(const Mask<EnumType>& other) const {
return Mask<EnumType>{mask_ ^ other.mask_};
}
constexpr Mask<EnumType> operator~() const { return Mask<EnumType>{~mask_}; }
// Assignment operators.
constexpr Mask<EnumType>& operator=(const Mask<EnumType>&) = default;
constexpr Mask<EnumType>& operator=(Mask<EnumType>&&) = default;
constexpr Mask<EnumType>& operator|=(const Mask<EnumType>& other) {
mask_ |= other.mask_;
return *this;
}
constexpr Mask<EnumType>& operator&=(const Mask<EnumType>& other) {
mask_ &= other.mask_;
return *this;
}
constexpr Mask<EnumType>& operator^=(const Mask<EnumType>& other) {
mask_ ^= other.mask_;
return *this;
}
private:
MaskType mask_ = {};
};
// Construction from Enum Types
template <typename EnumType>
inline constexpr Mask<EnumType> operator|(const EnumType& lhs,
const EnumType& rhs) {
return Mask<EnumType>{lhs} | rhs;
}
template <typename EnumType>
inline constexpr Mask<EnumType> operator&(const EnumType& lhs,
const EnumType& rhs) {
return Mask<EnumType>{lhs} & rhs;
}
template <typename EnumType>
inline constexpr Mask<EnumType> operator^(const EnumType& lhs,
const EnumType& rhs) {
return Mask<EnumType>{lhs} ^ rhs;
}
template <typename EnumType>
inline constexpr Mask<EnumType> operator~(const EnumType& other) {
return ~Mask<EnumType>{other};
}
template <typename EnumType>
inline constexpr Mask<EnumType> operator|(const EnumType& lhs,
const Mask<EnumType>& rhs) {
return Mask<EnumType>{lhs} | rhs;
}
template <typename EnumType>
inline constexpr Mask<EnumType> operator&(const EnumType& lhs,
const Mask<EnumType>& rhs) {
return Mask<EnumType>{lhs} & rhs;
}
template <typename EnumType>
inline constexpr Mask<EnumType> operator^(const EnumType& lhs,
const Mask<EnumType>& rhs) {
return Mask<EnumType>{lhs} ^ rhs;
}
// Relational operators with EnumType promotion. These can be replaced by a
// defaulted spaceship operator post C++20.
template <typename EnumType>
inline constexpr bool operator<(const EnumType& lhs,
const Mask<EnumType>& rhs) {
return Mask<EnumType>{lhs} < rhs;
}
template <typename EnumType>
inline constexpr bool operator>(const EnumType& lhs,
const Mask<EnumType>& rhs) {
return Mask<EnumType>{lhs} > rhs;
}
template <typename EnumType>
inline constexpr bool operator<=(const EnumType& lhs,
const Mask<EnumType>& rhs) {
return Mask<EnumType>{lhs} <= rhs;
}
template <typename EnumType>
inline constexpr bool operator>=(const EnumType& lhs,
const Mask<EnumType>& rhs) {
return Mask<EnumType>{lhs} >= rhs;
}
template <typename EnumType>
inline constexpr bool operator==(const EnumType& lhs,
const Mask<EnumType>& rhs) {
return Mask<EnumType>{lhs} == rhs;
}
template <typename EnumType>
inline constexpr bool operator!=(const EnumType& lhs,
const Mask<EnumType>& rhs) {
return Mask<EnumType>{lhs} != rhs;
}
} // namespace impeller
#endif // FLUTTER_IMPELLER_BASE_MASK_H_