#include #include #include #include #include #include #include #include #if MLN_RENDER_BACKEND_METAL #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif using namespace mbgl; TEST(OrderIndependentHash, Permutations) { // Try collections of up to this many elements constexpr int maxSize = 6; // Try this many different sets of values for each size constexpr int iterations = 10; // Use values up to this large constexpr size_t maxVal = 10; // Repeat all that with different random seeds constexpr int seeds = 5; for (int curSeed = 0; curSeed < seeds; ++curSeed) { std::seed_seq seed{0xf00dLL + curSeed}; std::default_random_engine engine(seed); std::uniform_int_distribution distribution(0, maxVal); for (int curSize = 1; curSize <= maxSize; ++curSize) { for (int ii = 0; ii < iterations; ++ii) { // Generate `curSize` random values std::vector values; for (int jj = 0; jj < curSize; ++jj) { values.push_back(distribution(engine)); } // Generate the hash for each permutation, comparing subsequent values to the first one. bool isFirst = true; size_t initialValue = 0; do { const auto curValue = util::order_independent_hash(values.begin(), values.end()); if (isFirst) { initialValue = curValue; isFirst = false; } else { EXPECT_EQ(initialValue, curValue); } } while (std::next_permutation(values.begin(), values.end())); // NOLINT(modernize-use-ranges) } } } } #if MLN_RENDER_BACKEND_METAL using namespace mbgl::shaders; template void each_subset(const TSet& sofar, TIter beg, TIter end, TFunc f) { if (beg == end) { f(sofar.cbegin(), sofar.cend()); } else { TSet with = sofar; with.insert(*beg); each_subset(with, std::next(beg), end, f); each_subset(sofar, std::next(beg), end, f); } } /// Ensure that no combination of attributes used by a shader definition produce a hash conflict template void checkShaderHashes() { using AttribSet = mbgl::unordered_set; AttribSet attributes; for (const auto& attrib : ShaderType::attributes) { attributes.insert(attrib.id); } std::set subsetHashes; each_subset(AttribSet{}, attributes.cbegin(), attributes.cend(), [&](auto beg, auto end) { if (beg != end) { std::vector subset(beg, end); std::optional firstHash; do { const auto hash = util::order_independent_hash(subset.begin(), subset.end()); if (firstHash) { // All permutations of the subset must yield the same hash key as the first EXPECT_EQ(*firstHash, hash); } else { firstHash.emplace(hash); // All subset hashes must be unique EXPECT_TRUE(subsetHashes.insert(hash).second); } } while (std::next_permutation(subset.begin(), subset.end())); // NOLINT(modernize-use-ranges) } }); EXPECT_EQ(static_cast(std::pow(2, attributes.size())), 1 + subsetHashes.size()); } template void checkShaderHashes() { // For OpenGL, we would need to do reflection //(checkShaderHashes>(),...); (checkShaderHashes>(), ...); } TEST(OrderIndependentHash, Shaders) { checkShaderHashes(); } #endif