Add TypedInteger
This CL adds a TypedInteger helper which provides additional type safety in Dawn. It is a compile-time restriction that prevents integers of different types from being used interchangably in Debug builds. It also adds ityp::{array,bitset,span} as helper classes to wrap std:: versions (not span). These accept a template paramter as the Index type so that typed integers, or enum classes, may be used as a type-safe index. For now, bind group layout binding indices use TypedInteger. Future CLs will convert other indices to be type-safe as well. Bug: dawn:442 Change-Id: I5b63b1e4f6154322db0227a7788a4e9b8303410e Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/19902 Reviewed-by: Austin Eng <enga@chromium.org> Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
parent
3f4f356611
commit
7a4685f448
|
@ -167,6 +167,10 @@ if (is_win || is_linux || is_mac || is_fuchsia || is_android) {
|
|||
"SwapChainUtils.h",
|
||||
"SystemUtils.cpp",
|
||||
"SystemUtils.h",
|
||||
"TypedInteger.h",
|
||||
"ityp_array.h",
|
||||
"ityp_bitset.h",
|
||||
"ityp_span.h",
|
||||
"vulkan_platform.h",
|
||||
"windows_with_undefs.h",
|
||||
"xlib_with_undefs.h",
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include "common/Assert.h"
|
||||
#include "common/Math.h"
|
||||
#include "common/UnderlyingType.h"
|
||||
|
||||
#include <bitset>
|
||||
#include <limits>
|
||||
|
@ -44,8 +45,11 @@ class BitSetIterator final {
|
|||
|
||||
bool operator==(const Iterator& other) const;
|
||||
bool operator!=(const Iterator& other) const;
|
||||
|
||||
T operator*() const {
|
||||
return static_cast<T>(mCurrentBit);
|
||||
using U = UnderlyingType<T>;
|
||||
ASSERT(mCurrentBit <= std::numeric_limits<U>::max());
|
||||
return static_cast<T>(static_cast<U>(mCurrentBit));
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -44,6 +44,11 @@ target_sources(dawn_common PRIVATE
|
|||
"SwapChainUtils.h"
|
||||
"SystemUtils.cpp"
|
||||
"SystemUtils.h"
|
||||
"TypedInteger.h"
|
||||
"UnderlyingType.h"
|
||||
"ityp_array.h"
|
||||
"ityp_bitset.h"
|
||||
"ityp_span.h"
|
||||
"vulkan_platform.h"
|
||||
"windows_with_undefs.h"
|
||||
"xlib_with_undefs.h"
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
// Copyright 2020 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef COMMON_TYPEDINTEGER_H_
|
||||
#define COMMON_TYPEDINTEGER_H_
|
||||
|
||||
#include "common/Assert.h"
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
// TypedInteger is helper class that provides additional type safety in Debug.
|
||||
// - Integers of different (Tag, BaseIntegerType) may not be used interoperably
|
||||
// - Allows casts only to the underlying type.
|
||||
// - Integers of the same (Tag, BaseIntegerType) may be compared or assigned.
|
||||
// This class helps ensure that the many types of indices in Dawn aren't mixed up and used
|
||||
// interchangably.
|
||||
// In Release builds, when DAWN_ENABLE_ASSERTS is not defined, TypedInteger is a passthrough
|
||||
// typedef of the underlying type.
|
||||
//
|
||||
// Example:
|
||||
// using UintA = TypedInteger<struct TypeA, uint32_t>;
|
||||
// using UintB = TypedInteger<struct TypeB, uint32_t>;
|
||||
//
|
||||
// in Release:
|
||||
// using UintA = uint32_t;
|
||||
// using UintB = uint32_t;
|
||||
//
|
||||
// in Debug:
|
||||
// using UintA = detail::TypedIntegerImpl<struct TypeA, uint32_t>;
|
||||
// using UintB = detail::TypedIntegerImpl<struct TypeB, uint32_t>;
|
||||
//
|
||||
// Assignment, construction, comparison, and arithmetic with TypedIntegerImpl are allowed
|
||||
// only for typed integers of exactly the same type. Further, they must be
|
||||
// created / cast explicitly; there is no implicit conversion.
|
||||
//
|
||||
// UintA a(2);
|
||||
// uint32_t aValue = static_cast<uint32_t>(a);
|
||||
//
|
||||
namespace detail {
|
||||
template <typename Tag, typename T>
|
||||
class TypedIntegerImpl;
|
||||
} // namespace detail
|
||||
|
||||
template <typename Tag, typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
|
||||
#if defined(DAWN_ENABLE_ASSERTS)
|
||||
using TypedInteger = detail::TypedIntegerImpl<Tag, T>;
|
||||
#else
|
||||
using TypedInteger = T;
|
||||
#endif
|
||||
|
||||
namespace detail {
|
||||
template <typename Tag, typename T>
|
||||
class TypedIntegerImpl {
|
||||
static_assert(std::is_integral<T>::value, "TypedInteger must be integral");
|
||||
T mValue;
|
||||
|
||||
public:
|
||||
constexpr TypedIntegerImpl() : mValue(0) {
|
||||
}
|
||||
|
||||
// Construction from non-narrowing integral types.
|
||||
template <typename I,
|
||||
typename = std::enable_if_t<
|
||||
std::is_integral<I>::value &&
|
||||
std::numeric_limits<I>::max() <= std::numeric_limits<T>::max() &&
|
||||
std::numeric_limits<I>::min() >= std::numeric_limits<T>::min()>>
|
||||
explicit constexpr TypedIntegerImpl(I rhs) : mValue(static_cast<T>(rhs)) {
|
||||
}
|
||||
|
||||
// Allow explicit casts only to the underlying type. If you're casting out of an
|
||||
// TypedInteger, you should know what what you're doing, and exactly what type you
|
||||
// expect.
|
||||
explicit constexpr operator T() const {
|
||||
return static_cast<T>(this->mValue);
|
||||
}
|
||||
|
||||
// Same-tag TypedInteger comparison operators
|
||||
#define TYPED_COMPARISON(op) \
|
||||
constexpr bool operator op(const TypedIntegerImpl& rhs) const { \
|
||||
return mValue op rhs.mValue; \
|
||||
}
|
||||
TYPED_COMPARISON(<)
|
||||
TYPED_COMPARISON(<=)
|
||||
TYPED_COMPARISON(>)
|
||||
TYPED_COMPARISON(>=)
|
||||
TYPED_COMPARISON(==)
|
||||
TYPED_COMPARISON(!=)
|
||||
#undef TYPED_COMPARISON
|
||||
|
||||
// Increment / decrement operators for for-loop iteration
|
||||
constexpr TypedIntegerImpl& operator++() {
|
||||
ASSERT(this->mValue < std::numeric_limits<T>::max());
|
||||
++this->mValue;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr TypedIntegerImpl operator++(int) {
|
||||
TypedIntegerImpl ret = *this;
|
||||
|
||||
ASSERT(this->mValue < std::numeric_limits<T>::max());
|
||||
++this->mValue;
|
||||
return ret;
|
||||
}
|
||||
|
||||
constexpr TypedIntegerImpl& operator--() {
|
||||
assert(this->mValue > std::numeric_limits<T>::min());
|
||||
--this->mValue;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr TypedIntegerImpl operator--(int) {
|
||||
TypedIntegerImpl ret = *this;
|
||||
|
||||
ASSERT(this->mValue > std::numeric_limits<T>::min());
|
||||
--this->mValue;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T2 = T>
|
||||
constexpr std::enable_if_t<std::is_signed<T2>::value, TypedIntegerImpl> operator-() const {
|
||||
static_assert(std::is_same<T, T2>::value, "");
|
||||
// The negation of the most negative value cannot be represented.
|
||||
ASSERT(this->mValue != std::numeric_limits<T>::min());
|
||||
return TypedIntegerImpl(-this->mValue);
|
||||
}
|
||||
|
||||
template <typename T2 = T>
|
||||
constexpr std::enable_if_t<std::is_unsigned<T2>::value, TypedIntegerImpl> operator+(
|
||||
TypedIntegerImpl rhs) const {
|
||||
static_assert(std::is_same<T, T2>::value, "");
|
||||
// Overflow would wrap around
|
||||
ASSERT(this->mValue + rhs.mValue >= this->mValue);
|
||||
|
||||
return TypedIntegerImpl(this->mValue + rhs.mValue);
|
||||
}
|
||||
|
||||
template <typename T2 = T>
|
||||
constexpr std::enable_if_t<std::is_unsigned<T2>::value, TypedIntegerImpl> operator-(
|
||||
TypedIntegerImpl rhs) const {
|
||||
static_assert(std::is_same<T, T2>::value, "");
|
||||
// Overflow would wrap around
|
||||
ASSERT(this->mValue - rhs.mValue <= this->mValue);
|
||||
return TypedIntegerImpl(this->mValue - rhs.mValue);
|
||||
}
|
||||
|
||||
template <typename T2 = T>
|
||||
constexpr std::enable_if_t<std::is_signed<T2>::value, TypedIntegerImpl> operator+(
|
||||
TypedIntegerImpl rhs) const {
|
||||
static_assert(std::is_same<T, T2>::value, "");
|
||||
if (this->mValue > 0) {
|
||||
// rhs is positive: |rhs| is at most the distance between max and |this|.
|
||||
// rhs is negative: (positive + negative) won't overflow
|
||||
ASSERT(rhs.mValue <= std::numeric_limits<T>::max() - this->mValue);
|
||||
} else {
|
||||
// rhs is postive: (negative + positive) won't underflow
|
||||
// rhs is negative: |rhs| isn't less than the (negative) distance between min
|
||||
// and |this|
|
||||
ASSERT(rhs.mValue >= std::numeric_limits<T>::min() - this->mValue);
|
||||
}
|
||||
return TypedIntegerImpl(this->mValue + rhs.mValue);
|
||||
}
|
||||
|
||||
template <typename T2 = T>
|
||||
constexpr std::enable_if_t<std::is_signed<T2>::value, TypedIntegerImpl> operator-(
|
||||
TypedIntegerImpl rhs) const {
|
||||
static_assert(std::is_same<T, T2>::value, "");
|
||||
if (this->mValue > 0) {
|
||||
// rhs is positive: positive minus positive won't overflow
|
||||
// rhs is negative: |rhs| isn't less than the (negative) distance between |this|
|
||||
// and max.
|
||||
ASSERT(rhs.mValue >= this->mValue - std::numeric_limits<T>::max());
|
||||
} else {
|
||||
// rhs is positive: |rhs| is at most the distance between min and |this|
|
||||
// rhs is negative: negative minus negative won't overflow
|
||||
ASSERT(rhs.mValue <= this->mValue - std::numeric_limits<T>::min());
|
||||
}
|
||||
return TypedIntegerImpl(this->mValue - rhs.mValue);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
namespace std {
|
||||
template <typename Tag, typename T>
|
||||
class numeric_limits<detail::TypedIntegerImpl<Tag, T>> : public numeric_limits<T> {
|
||||
public:
|
||||
static detail::TypedIntegerImpl<Tag, T> max() noexcept {
|
||||
return detail::TypedIntegerImpl<Tag, T>(std::numeric_limits<T>::max());
|
||||
}
|
||||
static detail::TypedIntegerImpl<Tag, T> min() noexcept {
|
||||
return detail::TypedIntegerImpl<Tag, T>(std::numeric_limits<T>::min());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Tag, typename T>
|
||||
class hash<detail::TypedIntegerImpl<Tag, T>> : private hash<T> {
|
||||
public:
|
||||
size_t operator()(detail::TypedIntegerImpl<Tag, T> value) const {
|
||||
return hash<T>::operator()(static_cast<T>(value));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // COMMON_TYPEDINTEGER_H_
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright 2020 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef COMMON_UNDERLYINGTYPE_H_
|
||||
#define COMMON_UNDERLYINGTYPE_H_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
// UnderlyingType is similar to std::underlying_type_t. It is a passthrough for already
|
||||
// integer types which simplifies getting the underlying primitive type for an arbitrary
|
||||
// template parameter. It includes a specialization for detail::TypedIntegerImpl which yields
|
||||
// the wrapped integer type.
|
||||
namespace detail {
|
||||
template <typename T, typename Enable = void>
|
||||
struct UnderlyingTypeImpl;
|
||||
|
||||
template <typename I>
|
||||
struct UnderlyingTypeImpl<I, typename std::enable_if_t<std::is_integral<I>::value>> {
|
||||
using type = I;
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
struct UnderlyingTypeImpl<E, typename std::enable_if_t<std::is_enum<E>::value>> {
|
||||
using type = std::underlying_type_t<E>;
|
||||
};
|
||||
|
||||
// Forward declare the TypedInteger impl.
|
||||
template <typename Tag, typename T>
|
||||
class TypedIntegerImpl;
|
||||
|
||||
template <typename Tag, typename I>
|
||||
struct UnderlyingTypeImpl<TypedIntegerImpl<Tag, I>> {
|
||||
using type = typename UnderlyingTypeImpl<I>::type;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <typename T>
|
||||
using UnderlyingType = typename detail::UnderlyingTypeImpl<T>::type;
|
||||
|
||||
#endif // COMMON_UNDERLYINGTYPE_H_
|
|
@ -0,0 +1,96 @@
|
|||
// Copyright 2020 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef COMMON_ITYP_ARRAY_H_
|
||||
#define COMMON_ITYP_ARRAY_H_
|
||||
|
||||
#include "common/TypedInteger.h"
|
||||
#include "common/UnderlyingType.h"
|
||||
|
||||
#include <array>
|
||||
#include <type_traits>
|
||||
|
||||
namespace ityp {
|
||||
|
||||
// ityp::array is a helper class that wraps std::array with the restriction that
|
||||
// indices must be a particular type |Index|. Dawn uses multiple flat maps of
|
||||
// index-->data, and this class helps ensure an indices cannot be passed interchangably
|
||||
// to a flat map of a different type.
|
||||
template <typename Index, typename Value, size_t Size>
|
||||
class array : private std::array<Value, Size> {
|
||||
using I = UnderlyingType<Index>;
|
||||
using Base = std::array<Value, Size>;
|
||||
|
||||
static_assert(Size <= std::numeric_limits<I>::max(), "");
|
||||
|
||||
public:
|
||||
constexpr array() = default;
|
||||
|
||||
template <typename... Values>
|
||||
constexpr array(Values&&... values) : Base{std::forward<Values>(values)...} {
|
||||
}
|
||||
|
||||
Value& operator[](Index i) {
|
||||
I index = static_cast<I>(i);
|
||||
ASSERT(index >= 0 && index < Size);
|
||||
return Base::operator[](index);
|
||||
}
|
||||
|
||||
constexpr const Value& operator[](Index i) const {
|
||||
I index = static_cast<I>(i);
|
||||
ASSERT(index >= 0 && index < Size);
|
||||
return Base::operator[](index);
|
||||
}
|
||||
|
||||
Value& at(Index i) {
|
||||
I index = static_cast<I>(i);
|
||||
ASSERT(index >= 0 && index < Size);
|
||||
return Base::at(index);
|
||||
}
|
||||
|
||||
constexpr const Value& at(Index i) const {
|
||||
I index = static_cast<I>(i);
|
||||
ASSERT(index >= 0 && index < Size);
|
||||
return Base::at(index);
|
||||
}
|
||||
|
||||
Value* begin() noexcept {
|
||||
return Base::begin();
|
||||
}
|
||||
|
||||
const Value* begin() const noexcept {
|
||||
return Base::begin();
|
||||
}
|
||||
|
||||
Value* end() noexcept {
|
||||
return Base::end();
|
||||
}
|
||||
|
||||
const Value* end() const noexcept {
|
||||
return Base::end();
|
||||
}
|
||||
|
||||
constexpr Index size() const {
|
||||
return Index(static_cast<I>(Size));
|
||||
}
|
||||
|
||||
using Base::back;
|
||||
using Base::data;
|
||||
using Base::empty;
|
||||
using Base::front;
|
||||
};
|
||||
|
||||
} // namespace ityp
|
||||
|
||||
#endif // COMMON_ITYP_ARRAY_H_
|
|
@ -0,0 +1,124 @@
|
|||
// Copyright 2020 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef COMMON_ITYP_BITSET_H_
|
||||
#define COMMON_ITYP_BITSET_H_
|
||||
|
||||
#include "common/BitSetIterator.h"
|
||||
#include "common/TypedInteger.h"
|
||||
#include "common/UnderlyingType.h"
|
||||
|
||||
namespace ityp {
|
||||
|
||||
// ityp::bitset is a helper class that wraps std::bitset with the restriction that
|
||||
// indices must be a particular type |Index|.
|
||||
template <typename Index, size_t N>
|
||||
class bitset : private std::bitset<N> {
|
||||
using I = UnderlyingType<Index>;
|
||||
using Base = std::bitset<N>;
|
||||
|
||||
static_assert(sizeof(I) <= sizeof(size_t), "");
|
||||
|
||||
constexpr bitset(const Base& rhs) : Base(rhs) {
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr bitset() noexcept : Base() {
|
||||
}
|
||||
|
||||
constexpr bitset(unsigned long long value) noexcept : Base(value) {
|
||||
}
|
||||
|
||||
constexpr bool operator[](Index i) const {
|
||||
return Base::operator[](static_cast<I>(i));
|
||||
}
|
||||
|
||||
typename Base::reference operator[](Index i) {
|
||||
return Base::operator[](static_cast<I>(i));
|
||||
}
|
||||
|
||||
bool test(Index i) const {
|
||||
return Base::test(static_cast<I>(i));
|
||||
}
|
||||
|
||||
using Base::all;
|
||||
using Base::any;
|
||||
using Base::count;
|
||||
using Base::none;
|
||||
using Base::size;
|
||||
|
||||
bitset& operator&=(const bitset& other) noexcept {
|
||||
return static_cast<bitset&>(Base::operator&=(static_cast<const Base&>(other)));
|
||||
}
|
||||
|
||||
bitset& operator|=(const bitset& other) noexcept {
|
||||
return static_cast<bitset&>(Base::operator|=(static_cast<const Base&>(other)));
|
||||
}
|
||||
|
||||
bitset& operator^=(const bitset& other) noexcept {
|
||||
return static_cast<bitset&>(Base::operator^=(static_cast<const Base&>(other)));
|
||||
}
|
||||
|
||||
bitset operator~() const noexcept {
|
||||
return bitset(*this).flip();
|
||||
}
|
||||
|
||||
bitset& set() noexcept {
|
||||
return static_cast<bitset&>(Base::set());
|
||||
}
|
||||
|
||||
bitset& set(Index i, bool value = true) {
|
||||
return static_cast<bitset&>(Base::set(static_cast<I>(i), value));
|
||||
}
|
||||
|
||||
bitset& reset() noexcept {
|
||||
return static_cast<bitset&>(Base::reset());
|
||||
}
|
||||
|
||||
bitset& reset(Index i) {
|
||||
return static_cast<bitset&>(Base::reset(static_cast<I>(i)));
|
||||
}
|
||||
|
||||
bitset& flip() noexcept {
|
||||
return static_cast<bitset&>(Base::flip());
|
||||
}
|
||||
|
||||
bitset& flip(Index i) {
|
||||
return static_cast<bitset&>(Base::flip(static_cast<I>(i)));
|
||||
}
|
||||
|
||||
using Base::to_string;
|
||||
using Base::to_ullong;
|
||||
using Base::to_ulong;
|
||||
|
||||
friend bitset operator&(const bitset& lhs, const bitset& rhs) noexcept {
|
||||
return bitset(static_cast<const Base&>(lhs) & static_cast<const Base&>(rhs));
|
||||
}
|
||||
|
||||
friend bitset operator|(const bitset& lhs, const bitset& rhs) noexcept {
|
||||
return bitset(static_cast<const Base&>(lhs) | static_cast<const Base&>(rhs));
|
||||
}
|
||||
|
||||
friend bitset operator^(const bitset& lhs, const bitset& rhs) noexcept {
|
||||
return bitset(static_cast<const Base&>(lhs) ^ static_cast<const Base&>(rhs));
|
||||
}
|
||||
|
||||
friend BitSetIterator<N, Index> IterateBitSet(const bitset& bitset) {
|
||||
return BitSetIterator<N, Index>(static_cast<const Base&>(bitset));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ityp
|
||||
|
||||
#endif // COMMON_ITYP_BITSET_H_
|
|
@ -0,0 +1,103 @@
|
|||
// Copyright 2020 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef COMMON_ITYP_SPAN_H_
|
||||
#define COMMON_ITYP_SPAN_H_
|
||||
|
||||
#include "common/TypedInteger.h"
|
||||
#include "common/UnderlyingType.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace ityp {
|
||||
|
||||
// ityp::span is a helper class that wraps an unowned packed array of type |Value|.
|
||||
// It stores the size and pointer to first element. It has the restriction that
|
||||
// indices must be a particular type |Index|. This provides a type-safe way to index
|
||||
// raw pointers.
|
||||
template <typename Index, typename Value>
|
||||
class span {
|
||||
using I = UnderlyingType<Index>;
|
||||
|
||||
public:
|
||||
constexpr span() : mData(nullptr), mSize(0) {
|
||||
}
|
||||
constexpr span(Value* data, Index size) : mData(data), mSize(size) {
|
||||
}
|
||||
|
||||
constexpr Value& operator[](Index i) const {
|
||||
ASSERT(i < mSize);
|
||||
return mData[static_cast<I>(i)];
|
||||
}
|
||||
|
||||
Value* data() noexcept {
|
||||
return mData;
|
||||
}
|
||||
|
||||
const Value* data() const noexcept {
|
||||
return mData;
|
||||
}
|
||||
|
||||
Value* begin() noexcept {
|
||||
return mData;
|
||||
}
|
||||
|
||||
const Value* begin() const noexcept {
|
||||
return mData;
|
||||
}
|
||||
|
||||
Value* end() noexcept {
|
||||
return mData + static_cast<I>(mSize);
|
||||
}
|
||||
|
||||
const Value* end() const noexcept {
|
||||
return mData + static_cast<I>(mSize);
|
||||
}
|
||||
|
||||
Value& front() {
|
||||
ASSERT(mData != nullptr);
|
||||
ASSERT(static_cast<I>(mSize) >= 0);
|
||||
return *mData;
|
||||
}
|
||||
|
||||
const Value& front() const {
|
||||
ASSERT(mData != nullptr);
|
||||
ASSERT(static_cast<I>(mSize) >= 0);
|
||||
return *mData;
|
||||
}
|
||||
|
||||
Value& back() {
|
||||
ASSERT(mData != nullptr);
|
||||
ASSERT(static_cast<I>(mSize) >= 0);
|
||||
return *(mData + static_cast<I>(mSize) - 1);
|
||||
}
|
||||
|
||||
const Value& back() const {
|
||||
ASSERT(mData != nullptr);
|
||||
ASSERT(static_cast<I>(mSize) >= 0);
|
||||
return *(mData + static_cast<I>(mSize) - 1);
|
||||
}
|
||||
|
||||
Index size() const {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
private:
|
||||
Value* mData;
|
||||
Index mSize;
|
||||
};
|
||||
|
||||
} // namespace ityp
|
||||
|
||||
#endif // COMMON_ITYP_SPAN_H_
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "common/Assert.h"
|
||||
#include "common/Math.h"
|
||||
#include "common/ityp_bitset.h"
|
||||
#include "dawn_native/BindGroupLayout.h"
|
||||
#include "dawn_native/Buffer.h"
|
||||
#include "dawn_native/Device.h"
|
||||
|
@ -153,13 +154,14 @@ namespace dawn_native {
|
|||
}
|
||||
|
||||
DAWN_TRY(device->ValidateObject(descriptor->layout));
|
||||
if (descriptor->entryCount != descriptor->layout->GetBindingCount()) {
|
||||
|
||||
if (BindingIndex(descriptor->entryCount) != descriptor->layout->GetBindingCount()) {
|
||||
return DAWN_VALIDATION_ERROR("numBindings mismatch");
|
||||
}
|
||||
|
||||
const BindGroupLayoutBase::BindingMap& bindingMap = descriptor->layout->GetBindingMap();
|
||||
|
||||
std::bitset<kMaxBindingsPerGroup> bindingsSet;
|
||||
ityp::bitset<BindingIndex, kMaxBindingsPerGroup> bindingsSet;
|
||||
for (uint32_t i = 0; i < descriptor->entryCount; ++i) {
|
||||
const BindGroupEntry& entry = descriptor->entries[i];
|
||||
|
||||
|
@ -223,7 +225,7 @@ namespace dawn_native {
|
|||
: ObjectBase(device),
|
||||
mLayout(descriptor->layout),
|
||||
mBindingData(mLayout->ComputeBindingDataPointers(bindingDataStart)) {
|
||||
for (BindingIndex i = 0; i < mLayout->GetBindingCount(); ++i) {
|
||||
for (BindingIndex i{0}; i < mLayout->GetBindingCount(); ++i) {
|
||||
// TODO(enga): Shouldn't be needed when bindings are tightly packed.
|
||||
// This is to fill Ref<ObjectBase> holes with nullptrs.
|
||||
new (&mBindingData.bindings[i]) Ref<ObjectBase>();
|
||||
|
@ -267,7 +269,7 @@ namespace dawn_native {
|
|||
BindGroupBase::~BindGroupBase() {
|
||||
if (mLayout) {
|
||||
ASSERT(!IsError());
|
||||
for (BindingIndex i = 0; i < mLayout->GetBindingCount(); ++i) {
|
||||
for (BindingIndex i{0}; i < mLayout->GetBindingCount(); ++i) {
|
||||
mBindingData.bindings[i].~Ref<ObjectBase>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#ifndef DAWNNATIVE_BINDGROUPANDSTORAGEBARRIERTRACKER_H_
|
||||
#define DAWNNATIVE_BINDGROUPANDSTORAGEBARRIERTRACKER_H_
|
||||
|
||||
#include "common/ityp_bitset.h"
|
||||
#include "dawn_native/BindGroup.h"
|
||||
#include "dawn_native/BindGroupTracker.h"
|
||||
#include "dawn_native/Buffer.h"
|
||||
|
@ -41,7 +42,7 @@ namespace dawn_native {
|
|||
|
||||
const BindGroupLayoutBase* layout = bindGroup->GetLayout();
|
||||
|
||||
for (BindingIndex bindingIndex = 0; bindingIndex < layout->GetBindingCount();
|
||||
for (BindingIndex bindingIndex{0}; bindingIndex < layout->GetBindingCount();
|
||||
++bindingIndex) {
|
||||
const BindingInfo& bindingInfo = layout->GetBindingInfo(bindingIndex);
|
||||
|
||||
|
@ -88,10 +89,13 @@ namespace dawn_native {
|
|||
}
|
||||
|
||||
protected:
|
||||
std::array<std::bitset<kMaxBindingsPerGroup>, kMaxBindGroups> mBindingsNeedingBarrier = {};
|
||||
std::array<std::array<wgpu::BindingType, kMaxBindingsPerGroup>, kMaxBindGroups>
|
||||
std::array<ityp::bitset<BindingIndex, kMaxBindingsPerGroup>, kMaxBindGroups>
|
||||
mBindingsNeedingBarrier = {};
|
||||
std::array<ityp::array<BindingIndex, wgpu::BindingType, kMaxBindingsPerGroup>,
|
||||
kMaxBindGroups>
|
||||
mBindingTypes = {};
|
||||
std::array<std::array<ObjectBase*, kMaxBindingsPerGroup>, kMaxBindGroups> mBindings = {};
|
||||
std::array<ityp::array<BindingIndex, ObjectBase*, kMaxBindingsPerGroup>, kMaxBindGroups>
|
||||
mBindings = {};
|
||||
};
|
||||
|
||||
} // namespace dawn_native
|
||||
|
|
|
@ -304,12 +304,12 @@ namespace dawn_native {
|
|||
|
||||
// This is a utility function to help ASSERT that the BGL-binding comparator places buffers
|
||||
// first.
|
||||
bool CheckBufferBindingsFirst(const BindingInfo* bindings, BindingIndex count) {
|
||||
ASSERT(count <= kMaxBindingsPerGroup);
|
||||
bool CheckBufferBindingsFirst(ityp::span<BindingIndex, const BindingInfo> bindings) {
|
||||
ASSERT(bindings.size() <= BindingIndex(kMaxBindingsPerGroup));
|
||||
|
||||
BindingIndex lastBufferIndex = 0;
|
||||
BindingIndex lastBufferIndex{0};
|
||||
BindingIndex firstNonBufferIndex = std::numeric_limits<BindingIndex>::max();
|
||||
for (BindingIndex i = 0; i < count; ++i) {
|
||||
for (BindingIndex i{0}; i < bindings.size(); ++i) {
|
||||
if (IsBufferBinding(bindings[i].type)) {
|
||||
lastBufferIndex = std::max(i, lastBufferIndex);
|
||||
} else {
|
||||
|
@ -334,8 +334,8 @@ namespace dawn_native {
|
|||
|
||||
std::sort(sortedBindings.begin(), sortedBindings.end(), SortBindingsCompare);
|
||||
|
||||
for (BindingIndex i = 0; i < mBindingCount; ++i) {
|
||||
const BindGroupLayoutEntry& binding = sortedBindings[i];
|
||||
for (BindingIndex i{0}; i < mBindingCount; ++i) {
|
||||
const BindGroupLayoutEntry& binding = sortedBindings[static_cast<uint32_t>(i)];
|
||||
mBindingInfo[i].type = binding.type;
|
||||
mBindingInfo[i].visibility = binding.visibility;
|
||||
mBindingInfo[i].textureComponentType =
|
||||
|
@ -385,7 +385,7 @@ namespace dawn_native {
|
|||
const auto& it = mBindingMap.emplace(BindingNumber(binding.binding), i);
|
||||
ASSERT(it.second);
|
||||
}
|
||||
ASSERT(CheckBufferBindingsFirst(mBindingInfo.data(), mBindingCount));
|
||||
ASSERT(CheckBufferBindingsFirst({mBindingInfo.data(), mBindingCount}));
|
||||
}
|
||||
|
||||
BindGroupLayoutBase::BindGroupLayoutBase(DeviceBase* device, ObjectBase::ErrorTag tag)
|
||||
|
@ -432,7 +432,7 @@ namespace dawn_native {
|
|||
if (a->GetBindingCount() != b->GetBindingCount()) {
|
||||
return false;
|
||||
}
|
||||
for (BindingIndex i = 0; i < a->GetBindingCount(); ++i) {
|
||||
for (BindingIndex i{0}; i < a->GetBindingCount(); ++i) {
|
||||
if (a->mBindingInfo[i] != b->mBindingInfo[i]) {
|
||||
return false;
|
||||
}
|
||||
|
@ -445,7 +445,9 @@ namespace dawn_native {
|
|||
}
|
||||
|
||||
BindingIndex BindGroupLayoutBase::GetDynamicBufferCount() const {
|
||||
return mDynamicStorageBufferCount + mDynamicUniformBufferCount;
|
||||
// This is a binding index because dynamic buffers are packed at the front of the binding
|
||||
// info.
|
||||
return static_cast<BindingIndex>(mDynamicStorageBufferCount + mDynamicUniformBufferCount);
|
||||
}
|
||||
|
||||
uint32_t BindGroupLayoutBase::GetDynamicUniformBufferCount() const {
|
||||
|
@ -459,20 +461,21 @@ namespace dawn_native {
|
|||
size_t BindGroupLayoutBase::GetBindingDataSize() const {
|
||||
// | ------ buffer-specific ----------| ------------ object pointers -------------|
|
||||
// | --- offsets + sizes -------------| --------------- Ref<ObjectBase> ----------|
|
||||
size_t objectPointerStart = mBufferCount * sizeof(BufferBindingData);
|
||||
size_t objectPointerStart = static_cast<uint32_t>(mBufferCount) * sizeof(BufferBindingData);
|
||||
ASSERT(IsAligned(objectPointerStart, alignof(Ref<ObjectBase>)));
|
||||
return objectPointerStart + mBindingCount * sizeof(Ref<ObjectBase>);
|
||||
return objectPointerStart + static_cast<uint32_t>(mBindingCount) * sizeof(Ref<ObjectBase>);
|
||||
}
|
||||
|
||||
BindGroupLayoutBase::BindingDataPointers BindGroupLayoutBase::ComputeBindingDataPointers(
|
||||
void* dataStart) const {
|
||||
BufferBindingData* bufferData = reinterpret_cast<BufferBindingData*>(dataStart);
|
||||
auto bindings = reinterpret_cast<Ref<ObjectBase>*>(bufferData + mBufferCount);
|
||||
auto bindings =
|
||||
reinterpret_cast<Ref<ObjectBase>*>(bufferData + static_cast<uint32_t>(mBufferCount));
|
||||
|
||||
ASSERT(IsPtrAligned(bufferData, alignof(BufferBindingData)));
|
||||
ASSERT(IsPtrAligned(bindings, alignof(Ref<ObjectBase>)));
|
||||
|
||||
return {bufferData, bindings};
|
||||
return {{bufferData, mBufferCount}, {bindings, mBindingCount}};
|
||||
}
|
||||
|
||||
} // namespace dawn_native
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include "common/Constants.h"
|
||||
#include "common/Math.h"
|
||||
#include "common/SlabAllocator.h"
|
||||
#include "common/ityp_array.h"
|
||||
#include "common/ityp_span.h"
|
||||
#include "dawn_native/BindingInfo.h"
|
||||
#include "dawn_native/CachedObject.h"
|
||||
#include "dawn_native/Error.h"
|
||||
|
@ -25,7 +27,6 @@
|
|||
|
||||
#include "dawn_native/dawn_platform.h"
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <map>
|
||||
|
||||
|
@ -60,7 +61,7 @@ namespace dawn_native {
|
|||
|
||||
const BindingInfo& GetBindingInfo(BindingIndex bindingIndex) const {
|
||||
ASSERT(!IsError());
|
||||
ASSERT(bindingIndex < kMaxBindingsPerGroup);
|
||||
ASSERT(bindingIndex < BindingIndex(kMaxBindingsPerGroup));
|
||||
return mBindingInfo[bindingIndex];
|
||||
}
|
||||
const BindingMap& GetBindingMap() const;
|
||||
|
@ -86,8 +87,8 @@ namespace dawn_native {
|
|||
};
|
||||
|
||||
struct BindingDataPointers {
|
||||
BufferBindingData* const bufferData = nullptr;
|
||||
Ref<ObjectBase>* const bindings = nullptr;
|
||||
ityp::span<BindingIndex, BufferBindingData> const bufferData = {};
|
||||
ityp::span<BindingIndex, Ref<ObjectBase>> const bindings = {};
|
||||
};
|
||||
|
||||
// Compute the amount of space / alignment required to store bindings for a bind group of
|
||||
|
@ -114,11 +115,11 @@ namespace dawn_native {
|
|||
BindGroupLayoutBase(DeviceBase* device, ObjectBase::ErrorTag tag);
|
||||
|
||||
BindingIndex mBindingCount;
|
||||
BindingIndex mBufferCount = 0; // |BindingIndex| because buffers are packed at the front.
|
||||
BindingIndex mBufferCount{0}; // |BindingIndex| because buffers are packed at the front.
|
||||
uint32_t mDynamicUniformBufferCount = 0;
|
||||
uint32_t mDynamicStorageBufferCount = 0;
|
||||
|
||||
std::array<BindingInfo, kMaxBindingsPerGroup> mBindingInfo;
|
||||
ityp::array<BindingIndex, BindingInfo, kMaxBindingsPerGroup> mBindingInfo;
|
||||
|
||||
// Map from BindGroupLayoutEntry.binding to packed indices.
|
||||
BindingMap mBindingMap;
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#ifndef DAWNNATIVE_BINDINGINFO_H_
|
||||
#define DAWNNATIVE_BINDINGINFO_H_
|
||||
|
||||
#include "common/Constants.h"
|
||||
#include "common/TypedInteger.h"
|
||||
#include "dawn_native/Format.h"
|
||||
#include "dawn_native/dawn_platform.h"
|
||||
|
||||
|
@ -22,14 +24,13 @@
|
|||
|
||||
namespace dawn_native {
|
||||
|
||||
// TODO(enga): Can we have strongly typed integers so you can't convert between them
|
||||
// by accident? And also range-assertions (ex. kMaxBindingsPerGroup) in Debug?
|
||||
|
||||
// Binding numbers in the shader and BindGroup/BindGroupLayoutDescriptors
|
||||
using BindingNumber = uint32_t;
|
||||
using BindingNumber = TypedInteger<struct BindingNumberT, uint32_t>;
|
||||
|
||||
// Binding numbers get mapped to a packed range of indices
|
||||
using BindingIndex = uint32_t;
|
||||
using BindingIndex = TypedInteger<struct BindingIndexT, uint32_t>;
|
||||
|
||||
static constexpr BindingIndex kMaxBindingsPerGroupTyped = BindingIndex(kMaxBindingsPerGroup);
|
||||
|
||||
struct BindingInfo {
|
||||
wgpu::ShaderStage visibility;
|
||||
|
|
|
@ -125,14 +125,15 @@ namespace dawn_native {
|
|||
ASSERT(count > 0);
|
||||
|
||||
// Data which BindGroupLayoutDescriptor will point to for creation
|
||||
std::array<std::array<BindGroupLayoutEntry, kMaxBindingsPerGroup>, kMaxBindGroups>
|
||||
std::array<ityp::array<BindingIndex, BindGroupLayoutEntry, kMaxBindingsPerGroup>,
|
||||
kMaxBindGroups>
|
||||
entryData = {};
|
||||
|
||||
// A map of bindings to the index in |entryData|
|
||||
std::array<std::map<BindingNumber, BindingIndex>, kMaxBindGroups> usedBindingsMap = {};
|
||||
|
||||
// A counter of how many bindings we've populated in |entryData|
|
||||
std::array<uint32_t, kMaxBindGroups> entryCounts = {};
|
||||
std::array<BindingIndex, kMaxBindGroups> entryCounts = {};
|
||||
|
||||
uint32_t bindGroupLayoutCount = 0;
|
||||
for (uint32_t moduleIndex = 0; moduleIndex < count; ++moduleIndex) {
|
||||
|
@ -149,7 +150,7 @@ namespace dawn_native {
|
|||
}
|
||||
|
||||
BindGroupLayoutEntry bindingSlot;
|
||||
bindingSlot.binding = bindingNumber;
|
||||
bindingSlot.binding = static_cast<uint32_t>(bindingNumber);
|
||||
|
||||
DAWN_TRY(ValidateBindingTypeWithShaderStageVisibility(
|
||||
bindingInfo.type, StageBit(module->GetExecutionModel())));
|
||||
|
@ -183,7 +184,7 @@ namespace dawn_native {
|
|||
}
|
||||
}
|
||||
|
||||
uint32_t currentBindingCount = entryCounts[group];
|
||||
BindingIndex currentBindingCount = entryCounts[group];
|
||||
entryData[group][currentBindingCount] = bindingSlot;
|
||||
|
||||
usedBindingsMap[group][bindingNumber] = currentBindingCount;
|
||||
|
@ -199,7 +200,7 @@ namespace dawn_native {
|
|||
for (uint32_t group = 0; group < bindGroupLayoutCount; ++group) {
|
||||
BindGroupLayoutDescriptor desc = {};
|
||||
desc.entries = entryData[group].data();
|
||||
desc.entryCount = entryCounts[group];
|
||||
desc.entryCount = static_cast<uint32_t>(entryCounts[group]);
|
||||
|
||||
// We should never produce a bad descriptor.
|
||||
ASSERT(!ValidateBindGroupLayoutDescriptor(device, &desc).IsError());
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "dawn_native/ProgrammablePassEncoder.h"
|
||||
|
||||
#include "common/BitSetIterator.h"
|
||||
#include "common/ityp_array.h"
|
||||
#include "dawn_native/BindGroup.h"
|
||||
#include "dawn_native/Buffer.h"
|
||||
#include "dawn_native/CommandBuffer.h"
|
||||
|
@ -29,8 +30,8 @@ namespace dawn_native {
|
|||
namespace {
|
||||
void TrackBindGroupResourceUsage(PassResourceUsageTracker* usageTracker,
|
||||
BindGroupBase* group) {
|
||||
for (BindingIndex bindingIndex = 0;
|
||||
bindingIndex < group->GetLayout()->GetBindingCount(); ++bindingIndex) {
|
||||
for (BindingIndex bindingIndex{0}; bindingIndex < group->GetLayout()->GetBindingCount();
|
||||
++bindingIndex) {
|
||||
wgpu::BindingType type = group->GetLayout()->GetBindingInfo(bindingIndex).type;
|
||||
|
||||
switch (type) {
|
||||
|
@ -131,8 +132,8 @@ namespace dawn_native {
|
|||
|
||||
void ProgrammablePassEncoder::SetBindGroup(uint32_t groupIndex,
|
||||
BindGroupBase* group,
|
||||
uint32_t dynamicOffsetCount,
|
||||
const uint32_t* dynamicOffsets) {
|
||||
uint32_t dynamicOffsetCountIn,
|
||||
const uint32_t* dynamicOffsetsIn) {
|
||||
mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
|
||||
if (GetDevice()->IsValidationEnabled()) {
|
||||
DAWN_TRY(GetDevice()->ValidateObject(group));
|
||||
|
@ -141,13 +142,16 @@ namespace dawn_native {
|
|||
return DAWN_VALIDATION_ERROR("Setting bind group over the max");
|
||||
}
|
||||
|
||||
ityp::span<BindingIndex, const uint32_t> dynamicOffsets(
|
||||
dynamicOffsetsIn, BindingIndex(dynamicOffsetCountIn));
|
||||
|
||||
// Dynamic offsets count must match the number required by the layout perfectly.
|
||||
const BindGroupLayoutBase* layout = group->GetLayout();
|
||||
if (layout->GetDynamicBufferCount() != dynamicOffsetCount) {
|
||||
if (layout->GetDynamicBufferCount() != dynamicOffsets.size()) {
|
||||
return DAWN_VALIDATION_ERROR("dynamicOffset count mismatch");
|
||||
}
|
||||
|
||||
for (BindingIndex i = 0; i < dynamicOffsetCount; ++i) {
|
||||
for (BindingIndex i{0}; i < dynamicOffsets.size(); ++i) {
|
||||
const BindingInfo& bindingInfo = layout->GetBindingInfo(i);
|
||||
|
||||
// BGL creation sorts bindings such that the dynamic buffer bindings are first.
|
||||
|
@ -185,10 +189,10 @@ namespace dawn_native {
|
|||
SetBindGroupCmd* cmd = allocator->Allocate<SetBindGroupCmd>(Command::SetBindGroup);
|
||||
cmd->index = groupIndex;
|
||||
cmd->group = group;
|
||||
cmd->dynamicOffsetCount = dynamicOffsetCount;
|
||||
if (dynamicOffsetCount > 0) {
|
||||
cmd->dynamicOffsetCount = dynamicOffsetCountIn;
|
||||
if (dynamicOffsetCountIn > 0) {
|
||||
uint32_t* offsets = allocator->AllocateData<uint32_t>(cmd->dynamicOffsetCount);
|
||||
memcpy(offsets, dynamicOffsets, dynamicOffsetCount * sizeof(uint32_t));
|
||||
memcpy(offsets, dynamicOffsetsIn, dynamicOffsetCountIn * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
TrackBindGroupResourceUsage(&mUsageTracker, group);
|
||||
|
|
|
@ -289,9 +289,10 @@ namespace dawn_native {
|
|||
}
|
||||
}
|
||||
|
||||
std::string GetShaderDeclarationString(size_t group, uint32_t binding) {
|
||||
std::string GetShaderDeclarationString(size_t group, BindingNumber binding) {
|
||||
std::ostringstream ostream;
|
||||
ostream << "the shader module declaration at set " << group << " binding " << binding;
|
||||
ostream << "the shader module declaration at set " << group << " binding "
|
||||
<< static_cast<uint32_t>(binding);
|
||||
return ostream.str();
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
|
|
@ -103,7 +103,7 @@ namespace dawn_native { namespace d3d12 {
|
|||
D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER);
|
||||
descriptorOffsets[Sampler] = 0;
|
||||
|
||||
for (BindingIndex bindingIndex = 0; bindingIndex < GetBindingCount(); ++bindingIndex) {
|
||||
for (BindingIndex bindingIndex{0}; bindingIndex < GetBindingCount(); ++bindingIndex) {
|
||||
const BindingInfo& bindingInfo = GetBindingInfo(bindingIndex);
|
||||
|
||||
if (bindingInfo.hasDynamicOffset) {
|
||||
|
@ -170,7 +170,8 @@ namespace dawn_native { namespace d3d12 {
|
|||
mBindGroupAllocator.Deallocate(bindGroup);
|
||||
}
|
||||
|
||||
const std::array<uint32_t, kMaxBindingsPerGroup>& BindGroupLayout::GetBindingOffsets() const {
|
||||
const ityp::array<BindingIndex, uint32_t, kMaxBindingsPerGroup>&
|
||||
BindGroupLayout::GetBindingOffsets() const {
|
||||
return mBindingOffsets;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ namespace dawn_native { namespace d3d12 {
|
|||
Count,
|
||||
};
|
||||
|
||||
const std::array<uint32_t, kMaxBindingsPerGroup>& GetBindingOffsets() const;
|
||||
const ityp::array<BindingIndex, uint32_t, kMaxBindingsPerGroup>& GetBindingOffsets() const;
|
||||
uint32_t GetCbvUavSrvDescriptorTableSize() const;
|
||||
uint32_t GetSamplerDescriptorTableSize() const;
|
||||
uint32_t GetCbvUavSrvDescriptorCount() const;
|
||||
|
@ -54,7 +54,7 @@ namespace dawn_native { namespace d3d12 {
|
|||
|
||||
private:
|
||||
~BindGroupLayout() override = default;
|
||||
std::array<uint32_t, kMaxBindingsPerGroup> mBindingOffsets;
|
||||
ityp::array<BindingIndex, uint32_t, kMaxBindingsPerGroup> mBindingOffsets;
|
||||
std::array<uint32_t, DescriptorType::Count> mDescriptorCounts;
|
||||
D3D12_DESCRIPTOR_RANGE mRanges[DescriptorType::Count];
|
||||
|
||||
|
|
|
@ -150,7 +150,7 @@ namespace dawn_native { namespace d3d12 {
|
|||
|
||||
if (mInCompute) {
|
||||
for (uint32_t index : IterateBitSet(mBindGroupLayoutsMask)) {
|
||||
for (uint32_t binding : IterateBitSet(mBindingsNeedingBarrier[index])) {
|
||||
for (BindingIndex binding : IterateBitSet(mBindingsNeedingBarrier[index])) {
|
||||
wgpu::BindingType bindingType = mBindingTypes[index][binding];
|
||||
switch (bindingType) {
|
||||
case wgpu::BindingType::StorageBuffer:
|
||||
|
@ -213,16 +213,18 @@ namespace dawn_native { namespace d3d12 {
|
|||
const PipelineLayout* pipelineLayout,
|
||||
uint32_t index,
|
||||
BindGroup* group,
|
||||
uint32_t dynamicOffsetCount,
|
||||
const uint64_t* dynamicOffsets) {
|
||||
ASSERT(dynamicOffsetCount == group->GetLayout()->GetDynamicBufferCount());
|
||||
uint32_t dynamicOffsetCountIn,
|
||||
const uint64_t* dynamicOffsetsIn) {
|
||||
ityp::span<BindingIndex, const uint64_t> dynamicOffsets(
|
||||
dynamicOffsetsIn, BindingIndex(dynamicOffsetCountIn));
|
||||
ASSERT(dynamicOffsets.size() == group->GetLayout()->GetDynamicBufferCount());
|
||||
|
||||
// Usually, the application won't set the same offsets many times,
|
||||
// so always try to apply dynamic offsets even if the offsets stay the same
|
||||
if (dynamicOffsetCount != 0) {
|
||||
if (dynamicOffsets.size() != BindingIndex(0)) {
|
||||
// Update dynamic offsets.
|
||||
// Dynamic buffer bindings are packed at the beginning of the layout.
|
||||
for (BindingIndex bindingIndex = 0; bindingIndex < dynamicOffsetCount;
|
||||
for (BindingIndex bindingIndex{0}; bindingIndex < dynamicOffsets.size();
|
||||
++bindingIndex) {
|
||||
const BindingInfo& bindingInfo =
|
||||
group->GetLayout()->GetBindingInfo(bindingIndex);
|
||||
|
|
|
@ -123,7 +123,7 @@ namespace dawn_native { namespace d3d12 {
|
|||
|
||||
// Init root descriptors in root signatures for dynamic buffer bindings.
|
||||
// These are packed at the beginning of the layout binding info.
|
||||
for (BindingIndex dynamicBindingIndex = 0;
|
||||
for (BindingIndex dynamicBindingIndex{0};
|
||||
dynamicBindingIndex < bindGroupLayout->GetDynamicBufferCount();
|
||||
++dynamicBindingIndex) {
|
||||
const BindingInfo& bindingInfo =
|
||||
|
@ -194,7 +194,7 @@ namespace dawn_native { namespace d3d12 {
|
|||
uint32_t PipelineLayout::GetDynamicRootParameterIndex(uint32_t group,
|
||||
BindingIndex bindingIndex) const {
|
||||
ASSERT(group < kMaxBindGroups);
|
||||
ASSERT(bindingIndex < kMaxBindingsPerGroup);
|
||||
ASSERT(bindingIndex < kMaxBindingsPerGroupTyped);
|
||||
ASSERT(GetBindGroupLayout(group)->GetBindingInfo(bindingIndex).hasDynamicOffset);
|
||||
ASSERT(GetBindGroupLayout(group)->GetBindingInfo(bindingIndex).visibility !=
|
||||
wgpu::ShaderStage::None);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#ifndef DAWNNATIVE_D3D12_PIPELINELAYOUTD3D12_H_
|
||||
#define DAWNNATIVE_D3D12_PIPELINELAYOUTD3D12_H_
|
||||
|
||||
#include "common/ityp_array.h"
|
||||
#include "dawn_native/BindingInfo.h"
|
||||
#include "dawn_native/PipelineLayout.h"
|
||||
#include "dawn_native/d3d12/d3d12_platform.h"
|
||||
|
@ -42,7 +43,7 @@ namespace dawn_native { namespace d3d12 {
|
|||
MaybeError Initialize();
|
||||
std::array<uint32_t, kMaxBindGroups> mCbvUavSrvRootParameterInfo;
|
||||
std::array<uint32_t, kMaxBindGroups> mSamplerRootParameterInfo;
|
||||
std::array<std::array<uint32_t, kMaxBindingsPerGroup>, kMaxBindGroups>
|
||||
std::array<ityp::array<BindingIndex, uint32_t, kMaxBindingsPerGroup>, kMaxBindGroups>
|
||||
mDynamicRootParameterIndices;
|
||||
ComPtr<ID3D12RootSignature> mRootSignature;
|
||||
};
|
||||
|
|
|
@ -180,13 +180,15 @@ namespace dawn_native { namespace d3d12 {
|
|||
"spvc"));
|
||||
if (forceStorageBufferAsUAV) {
|
||||
DAWN_TRY(CheckSpvcSuccess(
|
||||
mSpvcContext.SetHLSLForceStorageBufferAsUAV(group, bindingNumber),
|
||||
mSpvcContext.SetHLSLForceStorageBufferAsUAV(
|
||||
group, static_cast<uint32_t>(bindingNumber)),
|
||||
"Unable to force read-only storage buffer as UAV w/ spvc"));
|
||||
}
|
||||
} else {
|
||||
compiler->set_decoration(bindingInfo.id, spv::DecorationBinding, bindingOffset);
|
||||
if (forceStorageBufferAsUAV) {
|
||||
compiler->set_hlsl_force_storage_buffer_as_uav(group, bindingNumber);
|
||||
compiler->set_hlsl_force_storage_buffer_as_uav(
|
||||
group, static_cast<uint32_t>(bindingNumber));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -496,7 +496,7 @@ namespace dawn_native { namespace metal {
|
|||
// TODO(kainino@chromium.org): Maintain buffers and offsets arrays in BindGroup
|
||||
// so that we only have to do one setVertexBuffers and one setFragmentBuffers
|
||||
// call here.
|
||||
for (BindingIndex bindingIndex = 0;
|
||||
for (BindingIndex bindingIndex{0};
|
||||
bindingIndex < group->GetLayout()->GetBindingCount(); ++bindingIndex) {
|
||||
const BindingInfo& bindingInfo =
|
||||
group->GetLayout()->GetBindingInfo(bindingIndex);
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#ifndef DAWNNATIVE_METAL_PIPELINELAYOUTMTL_H_
|
||||
#define DAWNNATIVE_METAL_PIPELINELAYOUTMTL_H_
|
||||
|
||||
#include "common/ityp_array.h"
|
||||
#include "dawn_native/BindingInfo.h"
|
||||
#include "dawn_native/PipelineLayout.h"
|
||||
|
||||
#include "dawn_native/PerStage.h"
|
||||
|
@ -41,7 +43,7 @@ namespace dawn_native { namespace metal {
|
|||
PipelineLayout(Device* device, const PipelineLayoutDescriptor* descriptor);
|
||||
|
||||
using BindingIndexInfo =
|
||||
std::array<std::array<uint32_t, kMaxBindingsPerGroup>, kMaxBindGroups>;
|
||||
std::array<ityp::array<BindingIndex, uint32_t, kMaxBindingsPerGroup>, kMaxBindGroups>;
|
||||
const BindingIndexInfo& GetBindingIndexInfo(SingleShaderStage stage) const;
|
||||
|
||||
// The number of Metal vertex stage buffers used for the whole pipeline layout.
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace dawn_native { namespace metal {
|
|||
uint32_t textureIndex = 0;
|
||||
|
||||
for (uint32_t group : IterateBitSet(GetBindGroupLayoutsMask())) {
|
||||
for (BindingIndex bindingIndex = 0;
|
||||
for (BindingIndex bindingIndex{0};
|
||||
bindingIndex < GetBindGroupLayout(group)->GetBindingCount(); ++bindingIndex) {
|
||||
const BindingInfo& bindingInfo =
|
||||
GetBindGroupLayout(group)->GetBindingInfo(bindingIndex);
|
||||
|
|
|
@ -149,7 +149,7 @@ namespace dawn_native { namespace metal {
|
|||
shaderc_spvc_msl_resource_binding mslBinding;
|
||||
mslBinding.stage = ToSpvcExecutionModel(stage);
|
||||
mslBinding.desc_set = group;
|
||||
mslBinding.binding = bindingNumber;
|
||||
mslBinding.binding = static_cast<uint32_t>(bindingNumber);
|
||||
mslBinding.msl_buffer = mslBinding.msl_texture = mslBinding.msl_sampler =
|
||||
shaderIndex;
|
||||
DAWN_TRY(CheckSpvcSuccess(mSpvcContext.AddMSLResourceBinding(mslBinding),
|
||||
|
@ -158,7 +158,7 @@ namespace dawn_native { namespace metal {
|
|||
spirv_cross::MSLResourceBinding mslBinding;
|
||||
mslBinding.stage = SpirvExecutionModelForStage(stage);
|
||||
mslBinding.desc_set = group;
|
||||
mslBinding.binding = bindingNumber;
|
||||
mslBinding.binding = static_cast<uint32_t>(bindingNumber);
|
||||
mslBinding.msl_buffer = mslBinding.msl_texture = mslBinding.msl_sampler =
|
||||
shaderIndex;
|
||||
|
||||
|
|
|
@ -241,7 +241,7 @@ namespace dawn_native { namespace opengl {
|
|||
const auto& indices = ToBackend(mPipelineLayout)->GetBindingIndexInfo()[index];
|
||||
uint32_t currentDynamicOffsetIndex = 0;
|
||||
|
||||
for (BindingIndex bindingIndex = 0;
|
||||
for (BindingIndex bindingIndex{0};
|
||||
bindingIndex < group->GetLayout()->GetBindingCount(); ++bindingIndex) {
|
||||
const BindingInfo& bindingInfo =
|
||||
group->GetLayout()->GetBindingInfo(bindingIndex);
|
||||
|
|
|
@ -183,20 +183,29 @@ namespace dawn_native { namespace opengl {
|
|||
|
||||
gl.Uniform1i(location, textureUnit);
|
||||
|
||||
GLuint textureIndex =
|
||||
indices[combined.textureLocation.group][combined.textureLocation.binding];
|
||||
mUnitsForTextures[textureIndex].push_back(textureUnit);
|
||||
|
||||
bool shouldUseFiltering;
|
||||
{
|
||||
const BindGroupLayoutBase* bgl =
|
||||
layout->GetBindGroupLayout(combined.textureLocation.group);
|
||||
Format::Type componentType =
|
||||
bgl->GetBindingInfo(bgl->GetBindingIndex(combined.textureLocation.binding))
|
||||
.textureComponentType;
|
||||
bool shouldUseFiltering = componentType == Format::Type::Float;
|
||||
BindingIndex bindingIndex =
|
||||
bgl->GetBindingIndex(combined.textureLocation.binding);
|
||||
|
||||
GLuint samplerIndex =
|
||||
indices[combined.samplerLocation.group][combined.samplerLocation.binding];
|
||||
GLuint textureIndex = indices[combined.textureLocation.group][bindingIndex];
|
||||
mUnitsForTextures[textureIndex].push_back(textureUnit);
|
||||
|
||||
Format::Type componentType =
|
||||
bgl->GetBindingInfo(bindingIndex).textureComponentType;
|
||||
shouldUseFiltering = componentType == Format::Type::Float;
|
||||
}
|
||||
{
|
||||
const BindGroupLayoutBase* bgl =
|
||||
layout->GetBindGroupLayout(combined.samplerLocation.group);
|
||||
BindingIndex bindingIndex =
|
||||
bgl->GetBindingIndex(combined.samplerLocation.binding);
|
||||
|
||||
GLuint samplerIndex = indices[combined.samplerLocation.group][bindingIndex];
|
||||
mUnitsForSamplers[samplerIndex].push_back({textureUnit, shouldUseFiltering});
|
||||
}
|
||||
|
||||
textureUnit++;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace dawn_native { namespace opengl {
|
|||
for (uint32_t group : IterateBitSet(GetBindGroupLayoutsMask())) {
|
||||
const BindGroupLayoutBase* bgl = GetBindGroupLayout(group);
|
||||
|
||||
for (BindingIndex bindingIndex = 0; bindingIndex < bgl->GetBindingCount();
|
||||
for (BindingIndex bindingIndex{0}; bindingIndex < bgl->GetBindingCount();
|
||||
++bindingIndex) {
|
||||
switch (bgl->GetBindingInfo(bindingIndex).type) {
|
||||
case wgpu::BindingType::UniformBuffer:
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#include "dawn_native/PipelineLayout.h"
|
||||
|
||||
#include "common/ityp_array.h"
|
||||
#include "dawn_native/BindingInfo.h"
|
||||
#include "dawn_native/opengl/opengl_platform.h"
|
||||
|
||||
namespace dawn_native { namespace opengl {
|
||||
|
@ -28,7 +30,7 @@ namespace dawn_native { namespace opengl {
|
|||
PipelineLayout(Device* device, const PipelineLayoutDescriptor* descriptor);
|
||||
|
||||
using BindingIndexInfo =
|
||||
std::array<std::array<GLuint, kMaxBindingsPerGroup>, kMaxBindGroups>;
|
||||
std::array<ityp::array<BindingIndex, GLuint, kMaxBindingsPerGroup>, kMaxBindGroups>;
|
||||
const BindingIndexInfo& GetBindingIndexInfo() const;
|
||||
|
||||
GLuint GetTextureUnitsUsed() const;
|
||||
|
|
|
@ -24,9 +24,9 @@
|
|||
|
||||
namespace dawn_native { namespace opengl {
|
||||
|
||||
std::string GetBindingName(uint32_t group, uint32_t binding) {
|
||||
std::string GetBindingName(uint32_t group, BindingNumber bindingNumber) {
|
||||
std::ostringstream o;
|
||||
o << "dawn_binding_" << group << "_" << binding;
|
||||
o << "dawn_binding_" << group << "_" << static_cast<uint32_t>(bindingNumber);
|
||||
return o.str();
|
||||
}
|
||||
|
||||
|
@ -42,8 +42,9 @@ namespace dawn_native { namespace opengl {
|
|||
std::string CombinedSampler::GetName() const {
|
||||
std::ostringstream o;
|
||||
o << "dawn_combined";
|
||||
o << "_" << samplerLocation.group << "_" << samplerLocation.binding;
|
||||
o << "_with_" << textureLocation.group << "_" << textureLocation.binding;
|
||||
o << "_" << samplerLocation.group << "_" << static_cast<uint32_t>(samplerLocation.binding);
|
||||
o << "_with_" << textureLocation.group << "_"
|
||||
<< static_cast<uint32_t>(textureLocation.binding);
|
||||
return o.str();
|
||||
}
|
||||
|
||||
|
@ -143,12 +144,19 @@ namespace dawn_native { namespace opengl {
|
|||
mSpvcContext.GetDecoration(sampler.sampler_id,
|
||||
shaderc_spvc_decoration_descriptorset,
|
||||
&info.samplerLocation.group);
|
||||
uint32_t samplerBinding;
|
||||
mSpvcContext.GetDecoration(sampler.sampler_id, shaderc_spvc_decoration_binding,
|
||||
&info.samplerLocation.binding);
|
||||
&samplerBinding);
|
||||
info.samplerLocation.binding = BindingNumber(samplerBinding);
|
||||
|
||||
mSpvcContext.GetDecoration(sampler.image_id, shaderc_spvc_decoration_descriptorset,
|
||||
&info.textureLocation.group);
|
||||
|
||||
uint32_t textureBinding;
|
||||
mSpvcContext.GetDecoration(sampler.image_id, shaderc_spvc_decoration_binding,
|
||||
&info.textureLocation.binding);
|
||||
&textureBinding);
|
||||
info.textureLocation.binding = BindingNumber(textureBinding);
|
||||
|
||||
mSpvcContext.SetName(sampler.combined_id, info.GetName());
|
||||
}
|
||||
} else {
|
||||
|
@ -158,12 +166,12 @@ namespace dawn_native { namespace opengl {
|
|||
auto& info = mCombinedInfo.back();
|
||||
info.samplerLocation.group =
|
||||
compiler->get_decoration(combined.sampler_id, spv::DecorationDescriptorSet);
|
||||
info.samplerLocation.binding =
|
||||
compiler->get_decoration(combined.sampler_id, spv::DecorationBinding);
|
||||
info.samplerLocation.binding = BindingNumber(
|
||||
compiler->get_decoration(combined.sampler_id, spv::DecorationBinding));
|
||||
info.textureLocation.group =
|
||||
compiler->get_decoration(combined.image_id, spv::DecorationDescriptorSet);
|
||||
info.textureLocation.binding =
|
||||
compiler->get_decoration(combined.image_id, spv::DecorationBinding);
|
||||
info.textureLocation.binding = BindingNumber(
|
||||
compiler->get_decoration(combined.image_id, spv::DecorationBinding));
|
||||
compiler->set_name(combined.combined_id, info.GetName());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,11 +23,11 @@ namespace dawn_native { namespace opengl {
|
|||
|
||||
class Device;
|
||||
|
||||
std::string GetBindingName(uint32_t group, uint32_t binding);
|
||||
std::string GetBindingName(uint32_t group, BindingNumber bindingNumber);
|
||||
|
||||
struct BindingLocation {
|
||||
uint32_t group;
|
||||
uint32_t binding;
|
||||
BindingNumber binding;
|
||||
};
|
||||
bool operator<(const BindingLocation& a, const BindingLocation& b);
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ namespace dawn_native { namespace vulkan {
|
|||
const BindingInfo& bindingInfo = GetBindingInfo(bindingIndex);
|
||||
|
||||
VkDescriptorSetLayoutBinding* vkBinding = &bindings[numBindings];
|
||||
vkBinding->binding = bindingNumber;
|
||||
vkBinding->binding = static_cast<uint32_t>(bindingNumber);
|
||||
vkBinding->descriptorType =
|
||||
VulkanDescriptorType(bindingInfo.type, bindingInfo.hasDynamicOffset);
|
||||
vkBinding->descriptorCount = 1;
|
||||
|
@ -118,7 +118,7 @@ namespace dawn_native { namespace vulkan {
|
|||
// Compute the size of descriptor pools used for this layout.
|
||||
std::map<VkDescriptorType, uint32_t> descriptorCountPerType;
|
||||
|
||||
for (BindingIndex bindingIndex = 0; bindingIndex < GetBindingCount(); ++bindingIndex) {
|
||||
for (BindingIndex bindingIndex{0}; bindingIndex < GetBindingCount(); ++bindingIndex) {
|
||||
const BindingInfo& bindingInfo = GetBindingInfo(bindingIndex);
|
||||
VkDescriptorType vulkanType =
|
||||
VulkanDescriptorType(bindingInfo.type, bindingInfo.hasDynamicOffset);
|
||||
|
|
|
@ -52,7 +52,7 @@ namespace dawn_native { namespace vulkan {
|
|||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write.pNext = nullptr;
|
||||
write.dstSet = GetHandle();
|
||||
write.dstBinding = bindingNumber;
|
||||
write.dstBinding = static_cast<uint32_t>(bindingNumber);
|
||||
write.dstArrayElement = 0;
|
||||
write.descriptorCount = 1;
|
||||
write.descriptorType =
|
||||
|
|
|
@ -144,7 +144,8 @@ namespace dawn_native { namespace vulkan {
|
|||
mDynamicOffsetCounts, mDynamicOffsets);
|
||||
|
||||
for (uint32_t index : IterateBitSet(mBindGroupLayoutsMask)) {
|
||||
for (uint32_t bindingIndex : IterateBitSet(mBindingsNeedingBarrier[index])) {
|
||||
for (BindingIndex bindingIndex :
|
||||
IterateBitSet(mBindingsNeedingBarrier[index])) {
|
||||
switch (mBindingTypes[index][bindingIndex]) {
|
||||
case wgpu::BindingType::StorageBuffer:
|
||||
static_cast<Buffer*>(mBindings[index][bindingIndex])
|
||||
|
|
|
@ -159,6 +159,9 @@ test("dawn_unittests") {
|
|||
"unittests/ErrorTests.cpp",
|
||||
"unittests/ExtensionTests.cpp",
|
||||
"unittests/GetProcAddressTests.cpp",
|
||||
"unittests/ITypArrayTests.cpp",
|
||||
"unittests/ITypBitsetTests.cpp",
|
||||
"unittests/ITypSpanTests.cpp",
|
||||
"unittests/LinkedListTests.cpp",
|
||||
"unittests/MathTests.cpp",
|
||||
"unittests/ObjectBaseTests.cpp",
|
||||
|
@ -172,6 +175,7 @@ test("dawn_unittests") {
|
|||
"unittests/SlabAllocatorTests.cpp",
|
||||
"unittests/SystemUtilsTests.cpp",
|
||||
"unittests/ToBackendTests.cpp",
|
||||
"unittests/TypedIntegerTests.cpp",
|
||||
"unittests/validation/BindGroupValidationTests.cpp",
|
||||
"unittests/validation/BufferValidationTests.cpp",
|
||||
"unittests/validation/CommandBufferValidationTests.cpp",
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <gtest/gtest.h>
|
||||
|
||||
#include "common/BitSetIterator.h"
|
||||
#include "common/ityp_bitset.h"
|
||||
|
||||
// This is ANGLE's BitSetIterator_unittests.cpp file.
|
||||
|
||||
|
@ -48,7 +49,7 @@ TEST_F(BitSetIteratorTest, Iterator) {
|
|||
// Test an empty iterator.
|
||||
TEST_F(BitSetIteratorTest, EmptySet) {
|
||||
// We don't use the FAIL gtest macro here since it returns immediately,
|
||||
// causing an unreachable code warning in MSVS
|
||||
// causing an unreachable code warning in MSVC
|
||||
bool sawBit = false;
|
||||
for (unsigned long bit : IterateBitSet(mStateBits)) {
|
||||
DAWN_UNUSED(bit);
|
||||
|
@ -82,3 +83,137 @@ TEST_F(BitSetIteratorTest, NonLValueBitset) {
|
|||
|
||||
EXPECT_EQ((mStateBits & otherBits).count(), seenBits.size());
|
||||
}
|
||||
|
||||
class EnumBitSetIteratorTest : public testing::Test {
|
||||
protected:
|
||||
enum class TestEnum { A, B, C, D, E, F, G, H, I, J, EnumCount };
|
||||
|
||||
static constexpr size_t kEnumCount = static_cast<size_t>(TestEnum::EnumCount);
|
||||
ityp::bitset<TestEnum, kEnumCount> mStateBits;
|
||||
};
|
||||
|
||||
// Simple iterator test.
|
||||
TEST_F(EnumBitSetIteratorTest, Iterator) {
|
||||
std::set<TestEnum> originalValues;
|
||||
originalValues.insert(TestEnum::B);
|
||||
originalValues.insert(TestEnum::F);
|
||||
originalValues.insert(TestEnum::C);
|
||||
originalValues.insert(TestEnum::I);
|
||||
|
||||
for (TestEnum value : originalValues) {
|
||||
mStateBits.set(value);
|
||||
}
|
||||
|
||||
std::set<TestEnum> readValues;
|
||||
for (TestEnum bit : IterateBitSet(mStateBits)) {
|
||||
EXPECT_EQ(1u, originalValues.count(bit));
|
||||
EXPECT_EQ(0u, readValues.count(bit));
|
||||
readValues.insert(bit);
|
||||
}
|
||||
|
||||
EXPECT_EQ(originalValues.size(), readValues.size());
|
||||
}
|
||||
|
||||
// Test an empty iterator.
|
||||
TEST_F(EnumBitSetIteratorTest, EmptySet) {
|
||||
// We don't use the FAIL gtest macro here since it returns immediately,
|
||||
// causing an unreachable code warning in MSVC
|
||||
bool sawBit = false;
|
||||
for (TestEnum bit : IterateBitSet(mStateBits)) {
|
||||
DAWN_UNUSED(bit);
|
||||
sawBit = true;
|
||||
}
|
||||
EXPECT_FALSE(sawBit);
|
||||
}
|
||||
|
||||
// Test iterating a result of combining two bitsets.
|
||||
TEST_F(EnumBitSetIteratorTest, NonLValueBitset) {
|
||||
ityp::bitset<TestEnum, kEnumCount> otherBits;
|
||||
|
||||
mStateBits.set(TestEnum::B);
|
||||
mStateBits.set(TestEnum::C);
|
||||
mStateBits.set(TestEnum::D);
|
||||
mStateBits.set(TestEnum::E);
|
||||
|
||||
otherBits.set(TestEnum::A);
|
||||
otherBits.set(TestEnum::B);
|
||||
otherBits.set(TestEnum::D);
|
||||
otherBits.set(TestEnum::F);
|
||||
|
||||
std::set<TestEnum> seenBits;
|
||||
|
||||
for (TestEnum bit : IterateBitSet(mStateBits & otherBits)) {
|
||||
EXPECT_EQ(0u, seenBits.count(bit));
|
||||
seenBits.insert(bit);
|
||||
EXPECT_TRUE(mStateBits[bit]);
|
||||
EXPECT_TRUE(otherBits[bit]);
|
||||
}
|
||||
|
||||
EXPECT_EQ((mStateBits & otherBits).count(), seenBits.size());
|
||||
}
|
||||
|
||||
class ITypBitsetIteratorTest : public testing::Test {
|
||||
protected:
|
||||
using IntegerT = TypedInteger<struct Foo, uint32_t>;
|
||||
ityp::bitset<IntegerT, 40> mStateBits;
|
||||
};
|
||||
|
||||
// Simple iterator test.
|
||||
TEST_F(ITypBitsetIteratorTest, Iterator) {
|
||||
std::set<IntegerT> originalValues;
|
||||
originalValues.insert(IntegerT(2));
|
||||
originalValues.insert(IntegerT(6));
|
||||
originalValues.insert(IntegerT(8));
|
||||
originalValues.insert(IntegerT(35));
|
||||
|
||||
for (IntegerT value : originalValues) {
|
||||
mStateBits.set(value);
|
||||
}
|
||||
|
||||
std::set<IntegerT> readValues;
|
||||
for (IntegerT bit : IterateBitSet(mStateBits)) {
|
||||
EXPECT_EQ(1u, originalValues.count(bit));
|
||||
EXPECT_EQ(0u, readValues.count(bit));
|
||||
readValues.insert(bit);
|
||||
}
|
||||
|
||||
EXPECT_EQ(originalValues.size(), readValues.size());
|
||||
}
|
||||
|
||||
// Test an empty iterator.
|
||||
TEST_F(ITypBitsetIteratorTest, EmptySet) {
|
||||
// We don't use the FAIL gtest macro here since it returns immediately,
|
||||
// causing an unreachable code warning in MSVC
|
||||
bool sawBit = false;
|
||||
for (IntegerT bit : IterateBitSet(mStateBits)) {
|
||||
DAWN_UNUSED(bit);
|
||||
sawBit = true;
|
||||
}
|
||||
EXPECT_FALSE(sawBit);
|
||||
}
|
||||
|
||||
// Test iterating a result of combining two bitsets.
|
||||
TEST_F(ITypBitsetIteratorTest, NonLValueBitset) {
|
||||
ityp::bitset<IntegerT, 40> otherBits;
|
||||
|
||||
mStateBits.set(IntegerT(1));
|
||||
mStateBits.set(IntegerT(2));
|
||||
mStateBits.set(IntegerT(3));
|
||||
mStateBits.set(IntegerT(4));
|
||||
|
||||
otherBits.set(IntegerT(0));
|
||||
otherBits.set(IntegerT(1));
|
||||
otherBits.set(IntegerT(3));
|
||||
otherBits.set(IntegerT(5));
|
||||
|
||||
std::set<IntegerT> seenBits;
|
||||
|
||||
for (IntegerT bit : IterateBitSet(mStateBits & otherBits)) {
|
||||
EXPECT_EQ(0u, seenBits.count(bit));
|
||||
seenBits.insert(bit);
|
||||
EXPECT_TRUE(mStateBits[bit]);
|
||||
EXPECT_TRUE(otherBits[bit]);
|
||||
}
|
||||
|
||||
EXPECT_EQ((mStateBits & otherBits).count(), seenBits.size());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2020 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "common/TypedInteger.h"
|
||||
#include "common/ityp_array.h"
|
||||
|
||||
class ITypArrayTest : public testing::Test {
|
||||
protected:
|
||||
using Key = TypedInteger<struct KeyT, uint32_t>;
|
||||
using Val = TypedInteger<struct ValT, uint32_t>;
|
||||
using Array = ityp::array<Key, Val, 10>;
|
||||
|
||||
// Test that the expected array methods can be constexpr
|
||||
struct ConstexprTest {
|
||||
static constexpr Array kArr = {Val(0), Val(1), Val(2), Val(3), Val(4),
|
||||
Val(5), Val(6), Val(7), Val(8), Val(9)};
|
||||
|
||||
static_assert(kArr[Key(3)] == Val(3), "");
|
||||
static_assert(kArr.at(Key(7)) == Val(7), "");
|
||||
static_assert(kArr.size() == Key(10), "");
|
||||
};
|
||||
};
|
||||
|
||||
// Test that values can be set at an index and retrieved from the same index.
|
||||
TEST_F(ITypArrayTest, Indexing) {
|
||||
Array arr;
|
||||
{
|
||||
arr[Key(2)] = Val(5);
|
||||
arr[Key(1)] = Val(9);
|
||||
arr[Key(9)] = Val(2);
|
||||
|
||||
ASSERT_EQ(arr[Key(2)], Val(5));
|
||||
ASSERT_EQ(arr[Key(1)], Val(9));
|
||||
ASSERT_EQ(arr[Key(9)], Val(2));
|
||||
}
|
||||
{
|
||||
arr.at(Key(4)) = Val(5);
|
||||
arr.at(Key(3)) = Val(8);
|
||||
arr.at(Key(1)) = Val(7);
|
||||
|
||||
ASSERT_EQ(arr.at(Key(4)), Val(5));
|
||||
ASSERT_EQ(arr.at(Key(3)), Val(8));
|
||||
ASSERT_EQ(arr.at(Key(1)), Val(7));
|
||||
}
|
||||
}
|
||||
|
||||
// Test that the array can be iterated in order with a range-based for loop
|
||||
TEST_F(ITypArrayTest, RangeBasedIteration) {
|
||||
Array arr;
|
||||
|
||||
// Assign in a non-const range-based for loop
|
||||
uint32_t i = 0;
|
||||
for (Val& val : arr) {
|
||||
val = Val(i);
|
||||
}
|
||||
|
||||
// Check values in a const range-based for loop
|
||||
i = 0;
|
||||
for (Val val : static_cast<const Array&>(arr)) {
|
||||
ASSERT_EQ(val, arr[Key(i++)]);
|
||||
}
|
||||
}
|
||||
|
||||
// Test that begin/end/front/back/data return pointers/references to the correct elements.
|
||||
TEST_F(ITypArrayTest, BeginEndFrontBackData) {
|
||||
Array arr;
|
||||
|
||||
// non-const versions
|
||||
ASSERT_EQ(arr.begin(), &arr[Key(0)]);
|
||||
ASSERT_EQ(arr.end(), &arr[Key(0)] + static_cast<uint32_t>(arr.size()));
|
||||
ASSERT_EQ(&arr.front(), &arr[Key(0)]);
|
||||
ASSERT_EQ(&arr.back(), &arr[Key(9)]);
|
||||
ASSERT_EQ(arr.data(), &arr[Key(0)]);
|
||||
|
||||
// const versions
|
||||
const Array& constArr = arr;
|
||||
ASSERT_EQ(constArr.begin(), &constArr[Key(0)]);
|
||||
ASSERT_EQ(constArr.end(), &constArr[Key(0)] + static_cast<uint32_t>(constArr.size()));
|
||||
ASSERT_EQ(&constArr.front(), &constArr[Key(0)]);
|
||||
ASSERT_EQ(&constArr.back(), &constArr[Key(9)]);
|
||||
ASSERT_EQ(constArr.data(), &constArr[Key(0)]);
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
// Copyright 2020 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "common/TypedInteger.h"
|
||||
#include "common/ityp_bitset.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
class ITypBitsetTest : public testing::Test {
|
||||
protected:
|
||||
using Key = TypedInteger<struct KeyT, size_t>;
|
||||
using Bitset = ityp::bitset<Key, 9>;
|
||||
|
||||
// Test that the expected bitset methods can be constexpr
|
||||
struct ConstexprTest {
|
||||
static constexpr Bitset kBitset = {1 << 0 | 1 << 3 | 1 << 7 | 1 << 8};
|
||||
|
||||
static_assert(kBitset[Key(0)] == true, "");
|
||||
static_assert(kBitset[Key(1)] == false, "");
|
||||
static_assert(kBitset[Key(2)] == false, "");
|
||||
static_assert(kBitset[Key(3)] == true, "");
|
||||
static_assert(kBitset[Key(4)] == false, "");
|
||||
static_assert(kBitset[Key(5)] == false, "");
|
||||
static_assert(kBitset[Key(6)] == false, "");
|
||||
static_assert(kBitset[Key(7)] == true, "");
|
||||
static_assert(kBitset[Key(8)] == true, "");
|
||||
|
||||
static_assert(kBitset.size() == 9, "");
|
||||
};
|
||||
|
||||
void ExpectBits(const Bitset& bits, std::set<size_t> indices) {
|
||||
size_t mask = 0;
|
||||
|
||||
for (size_t i = 0; i < bits.size(); ++i) {
|
||||
if (indices.count(i) == 0) {
|
||||
ASSERT_FALSE(bits[Key(i)]) << i;
|
||||
ASSERT_FALSE(bits.test(Key(i))) << i;
|
||||
} else {
|
||||
mask |= (1 << i);
|
||||
ASSERT_TRUE(bits[Key(i)]) << i;
|
||||
ASSERT_TRUE(bits.test(Key(i))) << i;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_EQ(bits.to_ullong(), mask);
|
||||
ASSERT_EQ(bits.to_ulong(), mask);
|
||||
ASSERT_EQ(bits.count(), indices.size());
|
||||
ASSERT_EQ(bits.all(), indices.size() == bits.size());
|
||||
ASSERT_EQ(bits.any(), indices.size() != 0);
|
||||
ASSERT_EQ(bits.none(), indices.size() == 0);
|
||||
}
|
||||
};
|
||||
|
||||
// Test that by default no bits are set
|
||||
TEST_F(ITypBitsetTest, DefaultZero) {
|
||||
Bitset bits;
|
||||
ExpectBits(bits, {});
|
||||
}
|
||||
|
||||
// Test the bitset can be initialized with a bitmask
|
||||
TEST_F(ITypBitsetTest, InitializeByBits) {
|
||||
Bitset bits = {1 << 1 | 1 << 2 | 1 << 7};
|
||||
ExpectBits(bits, {1, 2, 7});
|
||||
}
|
||||
|
||||
// Test that bits can be set at an index and retrieved from the same index.
|
||||
TEST_F(ITypBitsetTest, Indexing) {
|
||||
Bitset bits;
|
||||
ExpectBits(bits, {});
|
||||
|
||||
bits[Key(2)] = true;
|
||||
bits[Key(4)] = false;
|
||||
bits.set(Key(1));
|
||||
bits.set(Key(7), true);
|
||||
bits.set(Key(8), false);
|
||||
|
||||
ExpectBits(bits, {1, 2, 7});
|
||||
|
||||
bits.reset(Key(2));
|
||||
bits.reset(Key(7));
|
||||
ExpectBits(bits, {1});
|
||||
}
|
||||
|
||||
// Test that bits can be flipped
|
||||
TEST_F(ITypBitsetTest, Flip) {
|
||||
Bitset bits = {1 << 1 | 1 << 2 | 1 << 7};
|
||||
ExpectBits(bits, {1, 2, 7});
|
||||
|
||||
bits.flip(Key(4));
|
||||
bits.flip(Key(1)); // false
|
||||
bits.flip(Key(6));
|
||||
bits.flip(Key(5));
|
||||
ExpectBits(bits, {2, 4, 5, 6, 7});
|
||||
|
||||
bits.flip();
|
||||
ExpectBits(bits, {0, 1, 3, 8});
|
||||
|
||||
ExpectBits(~bits, {2, 4, 5, 6, 7});
|
||||
}
|
||||
|
||||
// Test that all the bits can be set/reset.
|
||||
TEST_F(ITypBitsetTest, SetResetAll) {
|
||||
Bitset bits;
|
||||
|
||||
bits.set();
|
||||
|
||||
ASSERT_EQ(bits.count(), 9u);
|
||||
ASSERT_TRUE(bits.all());
|
||||
ASSERT_TRUE(bits.any());
|
||||
ASSERT_FALSE(bits.none());
|
||||
|
||||
for (Key i(0); i < Key(9); ++i) {
|
||||
ASSERT_TRUE(bits[i]);
|
||||
}
|
||||
|
||||
bits.reset();
|
||||
|
||||
ASSERT_EQ(bits.count(), 0u);
|
||||
ASSERT_FALSE(bits.all());
|
||||
ASSERT_FALSE(bits.any());
|
||||
ASSERT_TRUE(bits.none());
|
||||
|
||||
for (Key i(0); i < Key(9); ++i) {
|
||||
ASSERT_FALSE(bits[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Test And operations
|
||||
TEST_F(ITypBitsetTest, And) {
|
||||
Bitset bits = {1 << 1 | 1 << 2 | 1 << 7};
|
||||
ExpectBits(bits, {1, 2, 7});
|
||||
|
||||
Bitset bits2 = bits & Bitset{1 << 0 | 1 << 3 | 1 << 7};
|
||||
ExpectBits(bits2, {7});
|
||||
ExpectBits(bits, {1, 2, 7});
|
||||
|
||||
bits &= Bitset{1 << 1 | 1 << 6};
|
||||
ExpectBits(bits, {1});
|
||||
}
|
||||
|
||||
// Test Or operations
|
||||
TEST_F(ITypBitsetTest, Or) {
|
||||
Bitset bits = {1 << 1 | 1 << 2 | 1 << 7};
|
||||
ExpectBits(bits, {1, 2, 7});
|
||||
|
||||
Bitset bits2 = bits | Bitset{1 << 0 | 1 << 3 | 1 << 7};
|
||||
ExpectBits(bits2, {0, 1, 2, 3, 7});
|
||||
ExpectBits(bits, {1, 2, 7});
|
||||
|
||||
bits |= Bitset{1 << 1 | 1 << 6};
|
||||
ExpectBits(bits, {1, 2, 6, 7});
|
||||
}
|
||||
|
||||
// Test xor operations
|
||||
TEST_F(ITypBitsetTest, Xor) {
|
||||
Bitset bits = {1 << 1 | 1 << 2 | 1 << 7};
|
||||
ExpectBits(bits, {1, 2, 7});
|
||||
|
||||
Bitset bits2 = bits ^ Bitset { 1 << 0 | 1 << 3 | 1 << 7 };
|
||||
ExpectBits(bits2, {0, 1, 2, 3});
|
||||
ExpectBits(bits, {1, 2, 7});
|
||||
|
||||
bits ^= Bitset{1 << 1 | 1 << 6};
|
||||
ExpectBits(bits, {2, 6, 7});
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
// Copyright 2020 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "common/TypedInteger.h"
|
||||
#include "common/ityp_span.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
class ITypSpanTest : public testing::Test {
|
||||
protected:
|
||||
using Key = TypedInteger<struct KeyT, size_t>;
|
||||
using Val = TypedInteger<struct ValT, uint32_t>;
|
||||
using Span = ityp::span<Key, Val>;
|
||||
};
|
||||
|
||||
// Test that values can be set at an index and retrieved from the same index.
|
||||
TEST_F(ITypSpanTest, Indexing) {
|
||||
std::array<Val, 10> arr;
|
||||
Span span(arr.data(), Key(arr.size()));
|
||||
{
|
||||
span[Key(2)] = Val(5);
|
||||
span[Key(1)] = Val(9);
|
||||
span[Key(9)] = Val(2);
|
||||
|
||||
ASSERT_EQ(span[Key(2)], Val(5));
|
||||
ASSERT_EQ(span[Key(1)], Val(9));
|
||||
ASSERT_EQ(span[Key(9)], Val(2));
|
||||
}
|
||||
}
|
||||
|
||||
// Test that the span can be is iterated in order with a range-based for loop
|
||||
TEST_F(ITypSpanTest, RangeBasedIteration) {
|
||||
std::array<Val, 10> arr;
|
||||
Span span(arr.data(), Key(arr.size()));
|
||||
|
||||
// Assign in a non-const range-based for loop
|
||||
uint32_t i = 0;
|
||||
for (Val& val : span) {
|
||||
val = Val(i);
|
||||
}
|
||||
|
||||
// Check values in a const range-based for loop
|
||||
i = 0;
|
||||
for (Val val : static_cast<const Span&>(span)) {
|
||||
ASSERT_EQ(val, span[Key(i++)]);
|
||||
}
|
||||
}
|
||||
|
||||
// Test that begin/end/front/back/data return pointers/references to the correct elements.
|
||||
TEST_F(ITypSpanTest, BeginEndFrontBackData) {
|
||||
std::array<Val, 10> arr;
|
||||
Span span(arr.data(), Key(arr.size()));
|
||||
|
||||
// non-const versions
|
||||
ASSERT_EQ(span.begin(), &span[Key(0)]);
|
||||
ASSERT_EQ(span.end(), &span[Key(0)] + static_cast<size_t>(span.size()));
|
||||
ASSERT_EQ(&span.front(), &span[Key(0)]);
|
||||
ASSERT_EQ(&span.back(), &span[Key(9)]);
|
||||
ASSERT_EQ(span.data(), &span[Key(0)]);
|
||||
|
||||
// const versions
|
||||
const Span& constSpan = span;
|
||||
ASSERT_EQ(constSpan.begin(), &constSpan[Key(0)]);
|
||||
ASSERT_EQ(constSpan.end(), &constSpan[Key(0)] + static_cast<size_t>(constSpan.size()));
|
||||
ASSERT_EQ(&constSpan.front(), &constSpan[Key(0)]);
|
||||
ASSERT_EQ(&constSpan.back(), &constSpan[Key(9)]);
|
||||
ASSERT_EQ(constSpan.data(), &constSpan[Key(0)]);
|
||||
}
|
|
@ -0,0 +1,234 @@
|
|||
// Copyright 2020 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "common/TypedInteger.h"
|
||||
#include "common/UnderlyingType.h"
|
||||
|
||||
class TypedIntegerTest : public testing::Test {
|
||||
protected:
|
||||
using Unsigned = TypedInteger<struct UnsignedT, uint32_t>;
|
||||
using Signed = TypedInteger<struct SignedT, int32_t>;
|
||||
};
|
||||
|
||||
// Test that typed integers can be created and cast and the internal values are identical
|
||||
TEST_F(TypedIntegerTest, ConstructionAndCast) {
|
||||
Signed svalue(2);
|
||||
EXPECT_EQ(static_cast<int32_t>(svalue), 2);
|
||||
|
||||
Unsigned uvalue(7);
|
||||
EXPECT_EQ(static_cast<uint32_t>(uvalue), 7u);
|
||||
|
||||
static_assert(static_cast<int32_t>(Signed(3)) == 3, "");
|
||||
static_assert(static_cast<uint32_t>(Unsigned(28)) == 28, "");
|
||||
}
|
||||
|
||||
// Test typed integer comparison operators
|
||||
TEST_F(TypedIntegerTest, Comparison) {
|
||||
Unsigned value(8);
|
||||
|
||||
// Truthy usages of comparison operators
|
||||
EXPECT_TRUE(value < Unsigned(9));
|
||||
EXPECT_TRUE(value <= Unsigned(9));
|
||||
EXPECT_TRUE(value <= Unsigned(8));
|
||||
EXPECT_TRUE(value == Unsigned(8));
|
||||
EXPECT_TRUE(value >= Unsigned(8));
|
||||
EXPECT_TRUE(value >= Unsigned(7));
|
||||
EXPECT_TRUE(value > Unsigned(7));
|
||||
EXPECT_TRUE(value != Unsigned(7));
|
||||
|
||||
// Falsy usages of comparison operators
|
||||
EXPECT_FALSE(value >= Unsigned(9));
|
||||
EXPECT_FALSE(value > Unsigned(9));
|
||||
EXPECT_FALSE(value > Unsigned(8));
|
||||
EXPECT_FALSE(value != Unsigned(8));
|
||||
EXPECT_FALSE(value < Unsigned(8));
|
||||
EXPECT_FALSE(value < Unsigned(7));
|
||||
EXPECT_FALSE(value <= Unsigned(7));
|
||||
EXPECT_FALSE(value == Unsigned(7));
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, Arithmetic) {
|
||||
// Postfix Increment
|
||||
{
|
||||
Signed value(0);
|
||||
EXPECT_EQ(value++, Signed(0));
|
||||
EXPECT_EQ(value, Signed(1));
|
||||
}
|
||||
|
||||
// Prefix Increment
|
||||
{
|
||||
Signed value(0);
|
||||
EXPECT_EQ(++value, Signed(1));
|
||||
EXPECT_EQ(value, Signed(1));
|
||||
}
|
||||
|
||||
// Postfix Decrement
|
||||
{
|
||||
Signed value(0);
|
||||
EXPECT_EQ(value--, Signed(0));
|
||||
EXPECT_EQ(value, Signed(-1));
|
||||
}
|
||||
|
||||
// Prefix Decrement
|
||||
{
|
||||
Signed value(0);
|
||||
EXPECT_EQ(--value, Signed(-1));
|
||||
EXPECT_EQ(value, Signed(-1));
|
||||
}
|
||||
|
||||
// Signed addition
|
||||
{
|
||||
Signed a(3);
|
||||
Signed b(-4);
|
||||
Signed c = a + b;
|
||||
EXPECT_EQ(a, Signed(3));
|
||||
EXPECT_EQ(b, Signed(-4));
|
||||
EXPECT_EQ(c, Signed(-1));
|
||||
}
|
||||
|
||||
// Signed subtraction
|
||||
{
|
||||
Signed a(3);
|
||||
Signed b(-4);
|
||||
Signed c = a - b;
|
||||
EXPECT_EQ(a, Signed(3));
|
||||
EXPECT_EQ(b, Signed(-4));
|
||||
EXPECT_EQ(c, Signed(7));
|
||||
}
|
||||
|
||||
// Unsigned addition
|
||||
{
|
||||
Unsigned a(9);
|
||||
Unsigned b(3);
|
||||
Unsigned c = a + b;
|
||||
EXPECT_EQ(a, Unsigned(9));
|
||||
EXPECT_EQ(b, Unsigned(3));
|
||||
EXPECT_EQ(c, Unsigned(12));
|
||||
}
|
||||
|
||||
// Unsigned subtraction
|
||||
{
|
||||
Unsigned a(9);
|
||||
Unsigned b(2);
|
||||
Unsigned c = a - b;
|
||||
EXPECT_EQ(a, Unsigned(9));
|
||||
EXPECT_EQ(b, Unsigned(2));
|
||||
EXPECT_EQ(c, Unsigned(7));
|
||||
}
|
||||
|
||||
// Negation
|
||||
{
|
||||
Signed a(5);
|
||||
Signed b = -a;
|
||||
EXPECT_EQ(a, Signed(5));
|
||||
EXPECT_EQ(b, Signed(-5));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, NumericLimits) {
|
||||
EXPECT_EQ(std::numeric_limits<Unsigned>::max(), Unsigned(std::numeric_limits<uint32_t>::max()));
|
||||
EXPECT_EQ(std::numeric_limits<Unsigned>::min(), Unsigned(std::numeric_limits<uint32_t>::min()));
|
||||
EXPECT_EQ(std::numeric_limits<Signed>::max(), Signed(std::numeric_limits<int32_t>::max()));
|
||||
EXPECT_EQ(std::numeric_limits<Signed>::min(), Signed(std::numeric_limits<int32_t>::min()));
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, UnderlyingType) {
|
||||
static_assert(std::is_same<UnderlyingType<Unsigned>, uint32_t>::value, "");
|
||||
static_assert(std::is_same<UnderlyingType<Signed>, int32_t>::value, "");
|
||||
}
|
||||
|
||||
// Tests for bounds assertions on arithmetic overflow and underflow.
|
||||
#if defined(DAWN_ENABLE_ASSERTS)
|
||||
|
||||
TEST_F(TypedIntegerTest, IncrementUnsignedOverflow) {
|
||||
Unsigned value(std::numeric_limits<uint32_t>::max() - 1);
|
||||
|
||||
value++; // Doesn't overflow.
|
||||
EXPECT_DEATH(value++, ""); // Overflows.
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, IncrementSignedOverflow) {
|
||||
Signed value(std::numeric_limits<int32_t>::max() - 1);
|
||||
|
||||
value++; // Doesn't overflow.
|
||||
EXPECT_DEATH(value++, ""); // Overflows.
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, DecrementUnsignedUnderflow) {
|
||||
Unsigned value(std::numeric_limits<uint32_t>::min() + 1);
|
||||
|
||||
value--; // Doesn't underflow.
|
||||
EXPECT_DEATH(value--, ""); // Underflows.
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, DecrementSignedUnderflow) {
|
||||
Signed value(std::numeric_limits<int32_t>::min() + 1);
|
||||
|
||||
value--; // Doesn't underflow.
|
||||
EXPECT_DEATH(value--, ""); // Underflows.
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, UnsignedAdditionOverflow) {
|
||||
Unsigned value(std::numeric_limits<uint32_t>::max() - 1);
|
||||
|
||||
value + Unsigned(1); // Doesn't overflow.
|
||||
EXPECT_DEATH(value + Unsigned(2), ""); // Overflows.
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, UnsignedSubtractionUnderflow) {
|
||||
Unsigned value(1);
|
||||
|
||||
value - Unsigned(1); // Doesn't underflow.
|
||||
EXPECT_DEATH(value - Unsigned(2), ""); // Underflows.
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, SignedAdditionOverflow) {
|
||||
Signed value(std::numeric_limits<int32_t>::max() - 1);
|
||||
|
||||
value + Signed(1); // Doesn't overflow.
|
||||
EXPECT_DEATH(value + Signed(2), ""); // Overflows.
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, SignedAdditionUnderflow) {
|
||||
Signed value(std::numeric_limits<int32_t>::min() + 1);
|
||||
|
||||
value + Signed(-1); // Doesn't underflow.
|
||||
EXPECT_DEATH(value + Signed(-2), ""); // Underflows.
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, SignedSubtractionOverflow) {
|
||||
Signed value(std::numeric_limits<int32_t>::max() - 1);
|
||||
|
||||
value - Signed(-1); // Doesn't overflow.
|
||||
EXPECT_DEATH(value - Signed(-2), ""); // Overflows.
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, SignedSubtractionUnderflow) {
|
||||
Signed value(std::numeric_limits<int32_t>::min() + 1);
|
||||
|
||||
value - Signed(1); // Doesn't underflow.
|
||||
EXPECT_DEATH(value - Signed(2), ""); // Underflows.
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, NegationOverflow) {
|
||||
Signed maxValue(std::numeric_limits<int32_t>::max());
|
||||
-maxValue; // Doesn't underflow.
|
||||
|
||||
Signed minValue(std::numeric_limits<int32_t>::min());
|
||||
EXPECT_DEATH(-minValue, ""); // Overflows.
|
||||
}
|
||||
|
||||
#endif // defined(DAWN_ENABLE_ASSERTS)
|
Loading…
Reference in New Issue