// Copyright 2022 The Android Open Source Project // // 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 "vk_util.h" #include #include namespace gfxstream { namespace vk { namespace vk_util { namespace vk_fn_info { // Register a fake Vulkan function for testing. using PFN_vkGfxstreamTestFunc = PFN_vkCreateDevice; REGISTER_VK_FN_INFO(GfxstreamTestFunc, ("vkGfxstreamTestFunc", "vkGfxstreamTestFuncGOOGLE", "vkGfxstreamTestFuncGFXSTREAM")) constexpr auto vkGfxstreamTestFuncNames = vk_fn_info::GetVkFnInfo::names; namespace { using ::testing::_; using ::testing::MockFunction; using ::testing::Return; using ::testing::StrEq; using ::testing::StrNe; TEST(getVkInstanceProcAddrWithFallbackTest, ShouldReturnNullOnFailure) { VkInstance instance = reinterpret_cast(0x1234'0000); MockFunction> vkGetInstanceProcAddrAlwaysNULL; EXPECT_CALL(vkGetInstanceProcAddrAlwaysNULL, Call(instance, _)).WillRepeatedly(Return(nullptr)); EXPECT_EQ(getVkInstanceProcAddrWithFallback({}, instance), nullptr); EXPECT_EQ(getVkInstanceProcAddrWithFallback({nullptr, nullptr}, instance), nullptr); EXPECT_EQ(getVkInstanceProcAddrWithFallback( {vkGetInstanceProcAddrAlwaysNULL.AsStdFunction(), vkGetInstanceProcAddrAlwaysNULL.AsStdFunction()}, instance), nullptr); } TEST(getVkInstanceProcAddrWithFallbackTest, ShouldSkipNullVkGetInstanceProcAddr) { VkInstance instance = reinterpret_cast(0x1234'0000); PFN_vkGfxstreamTestFunc validFp = reinterpret_cast(0x4321'0000); MockFunction> vkGetInstanceProcAddrMock; EXPECT_CALL(vkGetInstanceProcAddrMock, Call(instance, _)) .WillRepeatedly(Return(reinterpret_cast(validFp))); EXPECT_EQ(getVkInstanceProcAddrWithFallback( {nullptr, vkGetInstanceProcAddrMock.AsStdFunction()}, instance), validFp); EXPECT_EQ(getVkInstanceProcAddrWithFallback( {vkGetInstanceProcAddrMock.AsStdFunction(), nullptr}, instance), validFp); } TEST(getVkInstanceProcAddrWithFallbackTest, ShouldSkipNullFpReturned) { VkInstance instance = reinterpret_cast(0x1234'0000); PFN_vkGfxstreamTestFunc validFp = reinterpret_cast(0x4321'0000); MockFunction> vkGetInstanceProcAddrMock; MockFunction> vkGetInstanceProcAddrAlwaysNULL; // We know that vkGfxstreamTest has different names. EXPECT_CALL(vkGetInstanceProcAddrMock, Call(instance, StrNe(std::get<1>(vkGfxstreamTestFuncNames)))) .WillRepeatedly(Return(nullptr)); EXPECT_CALL(vkGetInstanceProcAddrMock, Call(instance, StrEq(std::get<1>(vkGfxstreamTestFuncNames)))) .WillRepeatedly(Return(reinterpret_cast(validFp))); EXPECT_CALL(vkGetInstanceProcAddrAlwaysNULL, Call(instance, _)).WillRepeatedly(Return(nullptr)); EXPECT_EQ(getVkInstanceProcAddrWithFallback( {vkGetInstanceProcAddrMock.AsStdFunction(), vkGetInstanceProcAddrAlwaysNULL.AsStdFunction()}, instance), validFp); EXPECT_EQ(getVkInstanceProcAddrWithFallback( {vkGetInstanceProcAddrAlwaysNULL.AsStdFunction(), vkGetInstanceProcAddrMock.AsStdFunction()}, instance), validFp); } TEST(getVkInstanceProcAddrWithFallbackTest, firstVkInstanceProcAddrShouldTakeThePriority) { VkInstance instance = reinterpret_cast(0x1234'0000); PFN_vkGfxstreamTestFunc validFp1 = reinterpret_cast(0x4321'0000); PFN_vkGfxstreamTestFunc validFp2 = reinterpret_cast(0x3421'0070); MockFunction> vkGetInstanceProcAddrMock1; MockFunction> vkGetInstanceProcAddrMock2; EXPECT_CALL(vkGetInstanceProcAddrMock1, Call(instance, _)) .WillRepeatedly(Return(reinterpret_cast(validFp1))); EXPECT_CALL(vkGetInstanceProcAddrMock2, Call(instance, _)) .WillRepeatedly(Return(reinterpret_cast(validFp2))); EXPECT_EQ(getVkInstanceProcAddrWithFallback( {vkGetInstanceProcAddrMock1.AsStdFunction(), vkGetInstanceProcAddrMock2.AsStdFunction()}, instance), validFp1); } TEST(getVkInstanceProcAddrWithFallbackTest, firstNameShouldTakeThePriority) { VkInstance instance = reinterpret_cast(0x1234'0000); PFN_vkGfxstreamTestFunc validFps[] = {reinterpret_cast(0x4321'0000), reinterpret_cast(0x3421'0070), reinterpret_cast(0x2222'4321)}; MockFunction> vkGetInstanceProcAddrMock; EXPECT_CALL(vkGetInstanceProcAddrMock, Call(instance, StrEq(std::get<0>(vkGfxstreamTestFuncNames)))) .WillRepeatedly(Return(reinterpret_cast(validFps[0]))); ON_CALL(vkGetInstanceProcAddrMock, Call(instance, StrEq(std::get<1>(vkGfxstreamTestFuncNames)))) .WillByDefault(Return(reinterpret_cast(validFps[1]))); ON_CALL(vkGetInstanceProcAddrMock, Call(instance, StrEq(std::get<2>(vkGfxstreamTestFuncNames)))) .WillByDefault(Return(reinterpret_cast(validFps[2]))); EXPECT_EQ(getVkInstanceProcAddrWithFallback( {vkGetInstanceProcAddrMock.AsStdFunction()}, instance), validFps[0]); } TEST(VkCheckCallbacksDeathTest, deviceLostCallbackShouldBeCalled) { setVkCheckCallbacks(std::make_unique(VkCheckCallbacks{ .onVkErrorDeviceLost = [] { exit(43); }, })); EXPECT_EXIT(VK_CHECK(VK_ERROR_DEVICE_LOST), testing::ExitedWithCode(43), ""); } TEST(VkCheckCallbacksDeathTest, deviceLostCallbackShouldNotBeCalled) { // Default death function uses exit code 42 emugl::setDieFunction([] { exit(42); }); // Device lost death function uses exit code 43 setVkCheckCallbacks(std::make_unique(VkCheckCallbacks{ .onVkErrorDeviceLost = [] { exit(43); }, })); EXPECT_EXIT(VK_CHECK(VK_ERROR_OUT_OF_DEVICE_MEMORY), testing::ExitedWithCode(42), ""); } TEST(VkCheckCallbacksDeathTest, nullCallbacksShouldntCrash) { emugl::setDieFunction([] { exit(42); }); setVkCheckCallbacks(nullptr); EXPECT_EXIT(VK_CHECK(VK_ERROR_DEVICE_LOST), testing::ExitedWithCode(42), ""); } TEST(VkCheckCallbacksDeathTest, nullVkDeviceLostErrorCallbackShouldntCrash) { emugl::setDieFunction([] { exit(42); }); setVkCheckCallbacks( std::make_unique(VkCheckCallbacks{.onVkErrorDeviceLost = nullptr})); EXPECT_EXIT(VK_CHECK(VK_ERROR_DEVICE_LOST), testing::ExitedWithCode(42), ""); } template class ExampleCrtpClass1 : public U { public: void doCtrp1() { T& self = static_cast(*this); EXPECT_EQ(self.value, 42); self.doCtrp1WasCalled = true; } }; template class ExampleCrtpClass2 : public U { public: void doCtrp2() { T& self = static_cast(*this); EXPECT_EQ(self.value, 42); self.doCtrp2WasCalled = true; } }; template class ExampleCrtpClass3 : public U { public: void doCtrp3() { T& self = static_cast(*this); EXPECT_EQ(self.value, 42); self.doCtrp3WasCalled = true; } }; struct MultiCrtpTestStruct : MultiCrtp { void doCtrpMethods() { doCtrp1(); doCtrp2(); doCtrp3(); } int value = 42; bool doCtrp1WasCalled = false; bool doCtrp2WasCalled = false; bool doCtrp3WasCalled = false; }; TEST(MultiCrtp, MultiCrtp) { MultiCrtpTestStruct object; object.doCtrpMethods(); EXPECT_TRUE(object.doCtrp1WasCalled); EXPECT_TRUE(object.doCtrp2WasCalled); EXPECT_TRUE(object.doCtrp3WasCalled); } TEST(vk_util, vk_insert_struct) { VkDeviceCreateInfo deviceCi = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pNext = nullptr, }; VkPhysicalDeviceFeatures2 physicalDeviceFeature = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, .pNext = nullptr, }; vk_insert_struct(deviceCi, physicalDeviceFeature); ASSERT_EQ(deviceCi.pNext, &physicalDeviceFeature); ASSERT_EQ(physicalDeviceFeature.pNext, nullptr); VkPhysicalDeviceSamplerYcbcrConversionFeatures ycbcrFeature = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES, .pNext = nullptr, }; VkPhysicalDeviceDescriptorIndexingFeatures indexingFeatures = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES, .pNext = nullptr, }; vk_insert_struct(ycbcrFeature, indexingFeatures); ASSERT_EQ(ycbcrFeature.pNext, &indexingFeatures); ASSERT_EQ(indexingFeatures.pNext, nullptr); vk_insert_struct(deviceCi, ycbcrFeature); const VkBaseInStructure* base = reinterpret_cast(&deviceCi); ASSERT_EQ(base, reinterpret_cast(&deviceCi)); base = base->pNext; ASSERT_EQ(base, reinterpret_cast(&ycbcrFeature)); base = base->pNext; ASSERT_EQ(base, reinterpret_cast(&indexingFeatures)); base = base->pNext; ASSERT_EQ(base, reinterpret_cast(&physicalDeviceFeature)); base = base->pNext; ASSERT_EQ(base, nullptr); } } // namespace } // namespace vk_fn_info } // namespace vk_util } // namespace vk } // namespace gfxstream