// Copyright 2022 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 SRC_DAWN_NATIVE_CACHEKEY_H_ #define SRC_DAWN_NATIVE_CACHEKEY_H_ #include #include #include #include #include #include #include #include #include "dawn/common/TypedInteger.h" #include "dawn/common/ityp_array.h" namespace dawn::native { // Forward declare classes because of co-dependency. class CacheKey; class CachedObject; // Stream operator for CacheKey for debugging. std::ostream& operator<<(std::ostream& os, const CacheKey& key); // Overridable serializer struct that should be implemented for cache key serializable // types/classes. template class CacheKeySerializer { public: static void Serialize(CacheKey* key, const T& t); }; class CacheKey : public std::vector { public: using std::vector::vector; enum class Type { ComputePipeline, RenderPipeline, Shader }; template class UnsafeUnkeyedValue { public: UnsafeUnkeyedValue() = default; // NOLINTNEXTLINE(runtime/explicit) allow implicit construction to decrease verbosity UnsafeUnkeyedValue(T&& value) : mValue(std::forward(value)) {} const T& UnsafeGetValue() const { return mValue; } private: T mValue; }; template CacheKey& Record(const T& t) { CacheKeySerializer::Serialize(this, t); return *this; } template CacheKey& Record(const T& t, const Args&... args) { CacheKeySerializer::Serialize(this, t); return Record(args...); } // Records iterables by prepending the number of elements. Some common iterables are have a // CacheKeySerializer implemented to avoid needing to split them out when recording, i.e. // strings and CacheKeys, but they fundamentally do the same as this function. template CacheKey& RecordIterable(const IterableT& iterable) { // Always record the size of generic iterables as a size_t for now. Record(static_cast(iterable.size())); for (auto it = iterable.begin(); it != iterable.end(); ++it) { Record(*it); } return *this; } template CacheKey& RecordIterable(const ityp::array& iterable) { Record(static_cast(iterable.size())); for (auto it = iterable.begin(); it != iterable.end(); ++it) { Record(*it); } return *this; } template CacheKey& RecordIterable(const Ptr* ptr, size_t n) { Record(n); for (size_t i = 0; i < n; ++i) { Record(ptr[i]); } return *this; } }; template CacheKey::UnsafeUnkeyedValue UnsafeUnkeyedValue(T&& value) { return CacheKey::UnsafeUnkeyedValue(std::forward(value)); } // Specialized overload for CacheKey::UnsafeIgnoredValue which does nothing. template class CacheKeySerializer> { public: constexpr static void Serialize(CacheKey* key, const CacheKey::UnsafeUnkeyedValue&) {} }; // Specialized overload for fundamental types. template class CacheKeySerializer>> { public: static void Serialize(CacheKey* key, const T t) { const char* it = reinterpret_cast(&t); key->insert(key->end(), it, (it + sizeof(T))); } }; // Specialized overload for bitsets that are smaller than 64. template class CacheKeySerializer, std::enable_if_t<(N <= 64)>> { public: static void Serialize(CacheKey* key, const std::bitset& t) { key->Record(t.to_ullong()); } }; // Specialized overload for bitsets since using the built-in to_ullong have a size limit. template class CacheKeySerializer, std::enable_if_t<(N > 64)>> { public: static void Serialize(CacheKey* key, const std::bitset& t) { // Serializes the bitset into series of uint8_t, along with recording the size. static_assert(N > 0); key->Record(static_cast(N)); uint8_t value = 0; for (size_t i = 0; i < N; i++) { value <<= 1; // Explicitly convert to numeric since MSVC doesn't like mixing of bools. value |= t[i] ? 1 : 0; if (i % 8 == 7) { // Whenever we fill an 8 bit value, record it and zero it out. key->Record(value); value = 0; } } // Serialize the last value if we are not a multiple of 8. if (N % 8 != 0) { key->Record(value); } } }; // Specialized overload for enums. template class CacheKeySerializer>> { public: static void Serialize(CacheKey* key, const T t) { CacheKeySerializer>::Serialize( key, static_cast>(t)); } }; // Specialized overload for TypedInteger. template class CacheKeySerializer<::detail::TypedIntegerImpl> { public: static void Serialize(CacheKey* key, const ::detail::TypedIntegerImpl t) { CacheKeySerializer::Serialize(key, static_cast(t)); } }; // Specialized overload for pointers. Since we are serializing for a cache key, we always // serialize via value, not by pointer. To handle nullptr scenarios, we always serialize whether // the pointer was nullptr followed by the contents if applicable. template class CacheKeySerializer>> { public: static void Serialize(CacheKey* key, const T t) { key->Record(t == nullptr); if (t != nullptr) { CacheKeySerializer>>::Serialize(key, *t); } } }; // Specialized overload for fixed arrays of primitives. template class CacheKeySerializer>> { public: static void Serialize(CacheKey* key, const T (&t)[N]) { static_assert(N > 0); key->Record(static_cast(N)); const char* it = reinterpret_cast(t); key->insert(key->end(), it, it + sizeof(t)); } }; // Specialized overload for fixed arrays of non-primitives. template class CacheKeySerializer>> { public: static void Serialize(CacheKey* key, const T (&t)[N]) { static_assert(N > 0); key->Record(static_cast(N)); for (size_t i = 0; i < N; i++) { key->Record(t[i]); } } }; // Specialized overload for CachedObjects. template class CacheKeySerializer>> { public: static void Serialize(CacheKey* key, const T& t) { key->Record(t.GetCacheKey()); } }; // Specialized overload for std::vector. template class CacheKeySerializer> { public: static void Serialize(CacheKey* key, const std::vector& t) { key->RecordIterable(t); } }; } // namespace dawn::native #endif // SRC_DAWN_NATIVE_CACHEKEY_H_