diff --git a/src/dawn/native/BUILD.gn b/src/dawn/native/BUILD.gn index 180cefcaba..bc90499405 100644 --- a/src/dawn/native/BUILD.gn +++ b/src/dawn/native/BUILD.gn @@ -202,6 +202,8 @@ source_set("sources") { "BuddyMemoryAllocator.h", "Buffer.cpp", "Buffer.h", + "CacheKey.cpp", + "CacheKey.h", "CachedObject.cpp", "CachedObject.h", "CallbackTaskManager.cpp", diff --git a/src/dawn/native/CMakeLists.txt b/src/dawn/native/CMakeLists.txt index 38b63d83b7..f8ccb4c238 100644 --- a/src/dawn/native/CMakeLists.txt +++ b/src/dawn/native/CMakeLists.txt @@ -52,6 +52,8 @@ target_sources(dawn_native PRIVATE "Buffer.h" "CachedObject.cpp" "CachedObject.h" + "CacheKey.cpp", + "CacheKey.h", "CallbackTaskManager.cpp" "CallbackTaskManager.h" "CommandAllocator.cpp" diff --git a/src/dawn/native/CacheKey.cpp b/src/dawn/native/CacheKey.cpp new file mode 100644 index 0000000000..ff2cec0495 --- /dev/null +++ b/src/dawn/native/CacheKey.cpp @@ -0,0 +1,33 @@ +// 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. + +#include "dawn/native/CacheKey.h" + +namespace dawn::native { + + template <> + void CacheKeySerializer::Serialize(CacheKey* key, const std::string& t) { + std::string len = std::to_string(t.length()); + key->insert(key->end(), len.begin(), len.end()); + key->push_back('"'); + key->insert(key->end(), t.begin(), t.end()); + key->push_back('"'); + } + + template <> + void CacheKeySerializer::Serialize(CacheKey* key, const CacheKey& t) { + key->insert(key->end(), t.begin(), t.end()); + } + +} // namespace dawn::native diff --git a/src/dawn/native/CacheKey.h b/src/dawn/native/CacheKey.h new file mode 100644 index 0000000000..cb58711e28 --- /dev/null +++ b/src/dawn/native/CacheKey.h @@ -0,0 +1,93 @@ +// 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 DAWNNATIVE_CACHE_KEY_H_ +#define DAWNNATIVE_CACHE_KEY_H_ + +#include +#include + +#include "dawn/common/Compiler.h" + +namespace dawn::native { + + using CacheKey = std::vector; + + // Overridable serializer struct that should be implemented for cache key serializable + // types/classes. + template + struct CacheKeySerializer { + static void Serialize(CacheKey* key, const T& t); + }; + + // Specialized overload for integral types. Note that we are currently serializing as a string + // to avoid handling null termiantors. + template + struct CacheKeySerializer>> { + static void Serialize(CacheKey* key, const Integer i) { + std::string str = std::to_string(i); + key->insert(key->end(), str.begin(), str.end()); + } + }; + + // Specialized overload for floating point types. Note that we are currently serializing as a + // string to avoid handling null termiantors. + template + struct CacheKeySerializer>> { + static void Serialize(CacheKey* key, const Float f) { + std::string str = std::to_string(f); + key->insert(key->end(), str.begin(), str.end()); + } + }; + + // Specialized overload for string literals. Note we drop the null-terminator. + template + struct CacheKeySerializer { + static void Serialize(CacheKey* key, const char (&t)[N]) { + std::string len = std::to_string(N - 1); + key->insert(key->end(), len.begin(), len.end()); + key->push_back('"'); + key->insert(key->end(), t, t + N - 1); + key->push_back('"'); + } + }; + + // Helper template function that defers to underlying static functions. + template + void SerializeInto(CacheKey* key, const T& t) { + CacheKeySerializer::Serialize(key, t); + } + + // Given list of arguments of types with a free implementation of SerializeIntoImpl in the + // dawn::native namespace, serializes each argument and appends them to the CacheKey while + // prepending member ids before each argument. + template + CacheKey GetCacheKey(const Ts&... inputs) { + CacheKey key; + key.push_back('{'); + int memberId = 0; + auto Serialize = [&](const auto& input) { + std::string memberIdStr = (memberId == 0 ? "" : ",") + std::to_string(memberId) + ":"; + key.insert(key.end(), memberIdStr.begin(), memberIdStr.end()); + SerializeInto(&key, input); + memberId++; + }; + (Serialize(inputs), ...); + key.push_back('}'); + return key; + } + +} // namespace dawn::native + +#endif // DAWNNATIVE_CACHE_KEY_H_ diff --git a/src/dawn/tests/BUILD.gn b/src/dawn/tests/BUILD.gn index 01087b805a..ae104a4930 100644 --- a/src/dawn/tests/BUILD.gn +++ b/src/dawn/tests/BUILD.gn @@ -238,6 +238,7 @@ dawn_test("dawn_unittests") { "unittests/SystemUtilsTests.cpp", "unittests/ToBackendTests.cpp", "unittests/TypedIntegerTests.cpp", + "unittests/native/CacheKeyTests.cpp", "unittests/native/CommandBufferEncodingTests.cpp", "unittests/native/CreatePipelineAsyncTaskTests.cpp", "unittests/native/DestroyObjectTests.cpp", diff --git a/src/dawn/tests/unittests/native/CacheKeyTests.cpp b/src/dawn/tests/unittests/native/CacheKeyTests.cpp new file mode 100644 index 0000000000..3228e1e658 --- /dev/null +++ b/src/dawn/tests/unittests/native/CacheKeyTests.cpp @@ -0,0 +1,89 @@ +// 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. + +#include +#include + +#include + +#include "dawn/native/CacheKey.h" + +namespace dawn::native { + + // Testing classes/structs with serializing implemented for testing. + struct A {}; + template <> + void CacheKeySerializer::Serialize(CacheKey* key, const A& t) { + std::string str = "structA"; + key->insert(key->end(), str.begin(), str.end()); + } + + class B {}; + template <> + void CacheKeySerializer::Serialize(CacheKey* key, const B& t) { + std::string str = "classB"; + key->insert(key->end(), str.begin(), str.end()); + } + + namespace { + + // Matcher to compare CacheKey to a string for easier testing. + MATCHER_P(CacheKeyEq, + key, + "cache key " + std::string(negation ? "not" : "") + "equal to " + key) { + return std::string(arg.begin(), arg.end()) == key; + } + + TEST(CacheKeyTest, IntegralTypes) { + EXPECT_THAT(GetCacheKey((int)-1), CacheKeyEq("{0:-1}")); + EXPECT_THAT(GetCacheKey((uint8_t)2), CacheKeyEq("{0:2}")); + EXPECT_THAT(GetCacheKey((uint16_t)4), CacheKeyEq("{0:4}")); + EXPECT_THAT(GetCacheKey((uint32_t)8), CacheKeyEq("{0:8}")); + EXPECT_THAT(GetCacheKey((uint64_t)16), CacheKeyEq("{0:16}")); + + EXPECT_THAT(GetCacheKey((int)-1, (uint8_t)2, (uint16_t)4, (uint32_t)8, (uint64_t)16), + CacheKeyEq("{0:-1,1:2,2:4,3:8,4:16}")); + } + + TEST(CacheKeyTest, FloatingTypes) { + EXPECT_THAT(GetCacheKey((float)0.5), CacheKeyEq("{0:0.500000}")); + EXPECT_THAT(GetCacheKey((double)32.0), CacheKeyEq("{0:32.000000}")); + + EXPECT_THAT(GetCacheKey((float)0.5, (double)32.0), + CacheKeyEq("{0:0.500000,1:32.000000}")); + } + + TEST(CacheKeyTest, Strings) { + std::string str0 = "string0"; + std::string str1 = "string1"; + + EXPECT_THAT(GetCacheKey("string0"), CacheKeyEq(R"({0:7"string0"})")); + EXPECT_THAT(GetCacheKey(str0), CacheKeyEq(R"({0:7"string0"})")); + EXPECT_THAT(GetCacheKey("string0", str1), CacheKeyEq(R"({0:7"string0",1:7"string1"})")); + } + + TEST(CacheKeyTest, NestedCacheKey) { + EXPECT_THAT(GetCacheKey(GetCacheKey((int)-1)), CacheKeyEq("{0:{0:-1}}")); + EXPECT_THAT(GetCacheKey(GetCacheKey("string")), CacheKeyEq(R"({0:{0:6"string"}})")); + EXPECT_THAT(GetCacheKey(GetCacheKey(A{})), CacheKeyEq("{0:{0:structA}}")); + EXPECT_THAT(GetCacheKey(GetCacheKey(B())), CacheKeyEq("{0:{0:classB}}")); + + EXPECT_THAT(GetCacheKey(GetCacheKey((int)-1), GetCacheKey("string"), GetCacheKey(A{}), + GetCacheKey(B())), + CacheKeyEq(R"({0:{0:-1},1:{0:6"string"},2:{0:structA},3:{0:classB}})")); + } + + } // namespace + +} // namespace dawn::native