blob: 80fe2f447a43ef16fce7acb0b317982941edf3b5 [file] [log] [blame]
/* Copyright (c) 2015-2017, 2019-2026 The Khronos Group Inc.
* Copyright (c) 2015-2017, 2019-2026 Valve Corporation
* Copyright (c) 2015-2017, 2019-2026 LunarG, Inc.
* Modifications Copyright (C) 2022 RasterGrid Kft.
* Modifications Copyright (C) 2025-2026 Advanced Micro Devices, Inc. All rights reserved.
*
* 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.
*/
#pragma once
#include <bitset>
#include <cassert>
#include <cstdint>
#include <limits>
#include <type_traits>
#ifdef WIN32
#include <intrin.h> // For __lzcnt()
#else
#include <strings.h> // For ffs()
#endif
#include <vulkan/vulkan_core.h>
template <typename T>
constexpr bool IsPowerOfTwo(T x) {
static_assert(std::is_integral_v<T> && std::is_unsigned_v<T>, "Unsigned integer required");
return x && !(x & (x - 1));
}
template <typename T>
constexpr uint32_t GetBitSetCount(T value) {
static_assert(std::is_integral_v<T> && std::is_unsigned_v<T>, "Unsigned integer required");
static_assert(sizeof(T) == 4 || sizeof(T) == 8, "32 or 64 bit value is expected");
return static_cast<uint32_t>(std::bitset<sizeof(T) * 8>(value).count());
}
template <typename T>
constexpr bool IsSingleBitSet(T flags) {
static_assert(std::is_integral_v<T> && std::is_unsigned_v<T>, "Unsigned integer required");
return IsPowerOfTwo(flags);
}
// Returns the 0-based index of the MSB, like the x86 bit scan reverse (bsr) instruction
// Note: an input mask of 0 yields -1
[[maybe_unused]] static inline int MostSignificantBit(uint32_t mask) {
#if defined __GNUC__
return mask ? __builtin_clz(mask) ^ 31 : -1;
#elif defined _MSC_VER
unsigned long bit_pos;
return _BitScanReverse(&bit_pos, mask) ? int(bit_pos) : -1;
#else
for (int k = 31; k >= 0; --k) {
if (((mask >> k) & 1) != 0) {
return k;
}
}
return -1;
#endif
}
static inline int u_ffs(int val) {
#ifdef WIN32
unsigned long bit_pos = 0;
if (_BitScanForward(&bit_pos, val) != 0) {
bit_pos += 1;
}
return bit_pos;
#elif defined __GNUC__
return __builtin_ffs(val);
#else
return ffs(val);
#endif
}
// Given p2 a power of two, returns smallest multiple of p2 greater than or equal to x
// Different than std::align in that it simply aligns an unsigned integer, when std::align aligns a virtual address and does the
// necessary bookkeeping to be able to correctly free memory at the new address
template <typename T>
constexpr T Align(T x, T p2) {
static_assert(std::numeric_limits<T>::is_integer, "Unsigned integer required.");
static_assert(std::is_unsigned<T>::value, "Unsigned integer required.");
assert(IsPowerOfTwo(p2));
return (x + p2 - 1) & ~(p2 - 1);
}
// Given any positive p (not necessarily power of two),
// returns the smallest multiple of p greater than or equal to x
template <typename T>
constexpr T AlignToMultiple(T x, T p) {
static_assert(std::numeric_limits<T>::is_integer, "Unsigned integer required.");
static_assert(std::is_unsigned<T>::value, "Unsigned integer required.");
return ((x + p - 1) / p) * p;
}
// Returns the 0-based index of the LSB. An input mask of 0 yields -1
static inline int LeastSignificantBit(uint32_t mask) { return u_ffs(static_cast<int>(mask)) - 1; }
template <typename FlagBits, typename Flags>
FlagBits LeastSignificantFlag(Flags flags) {
const int bit_shift = LeastSignificantBit(flags);
assert(bit_shift != -1);
return static_cast<FlagBits>(1ull << bit_shift);
}
// Check if size is in range
static inline bool IsBetweenInclusive(VkDeviceSize value, VkDeviceSize min, VkDeviceSize max) {
return (value >= min) && (value <= max);
}
static inline bool IsBetweenInclusive(const VkExtent2D& value, const VkExtent2D& min, const VkExtent2D& max) {
return IsBetweenInclusive(value.width, min.width, max.width) && IsBetweenInclusive(value.height, min.height, max.height);
}
static inline bool IsBetweenInclusive(float value, float min, float max) { return (value >= min) && (value <= max); }
// Check if value is integer multiple of granularity
static inline bool IsIntegerMultipleOf(VkDeviceSize value, VkDeviceSize granularity) {
if (granularity == 0) {
return value == 0;
} else {
return (value % granularity) == 0;
}
}
static inline bool IsIntegerMultipleOf(const VkOffset2D& value, const VkOffset2D& granularity) {
return IsIntegerMultipleOf(value.x, granularity.x) && IsIntegerMultipleOf(value.y, granularity.y);
}
static inline bool IsPointerAligned(const void* address, VkDeviceSize alignment) {
auto ptr = reinterpret_cast<std::uintptr_t>(address);
return alignment != 0 && (ptr % alignment == 0);
}
static inline bool IsPointerAligned(VkDeviceAddress address, VkDeviceSize alignment) {
return alignment != 0 && (address % alignment == 0);
}
static inline VkDeviceSize SafeDivision(VkDeviceSize dividend, VkDeviceSize divisor) {
VkDeviceSize result = 0;
if (divisor != 0) {
result = dividend / divisor;
}
return result;
}
// For spots we care if one pointer is null, or if both are not null, are the same values
static inline bool EqualValuesOrBothNull(const uint32_t* a, const uint32_t* b) {
if (!a || !b) {
return a == b;
}
return *a == *b;
}
template <typename T>
constexpr T AbsDiff(T a, T b) {
static_assert(std::is_unsigned_v<T>);
return a > b ? a - b : b - a;
}
static inline uint32_t GetSmallestGreaterOrEquallPowerOfTwo(uint32_t v) {
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}