| // 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 <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include "vk_util.h" |
| |
| #include <vulkan/vulkan_core.h> |
| |
| #include <tuple> |
| |
| 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<GfxstreamTestFunc>::names; |
| |
| namespace { |
| using ::testing::_; |
| using ::testing::MockFunction; |
| using ::testing::Return; |
| using ::testing::StrEq; |
| using ::testing::StrNe; |
| |
| TEST(getVkInstanceProcAddrWithFallbackTest, ShouldReturnNullOnFailure) { |
| VkInstance instance = reinterpret_cast<VkInstance>(0x1234'0000); |
| MockFunction<std::remove_pointer_t<PFN_vkGetInstanceProcAddr>> vkGetInstanceProcAddrAlwaysNULL; |
| |
| EXPECT_CALL(vkGetInstanceProcAddrAlwaysNULL, Call(instance, _)).WillRepeatedly(Return(nullptr)); |
| |
| EXPECT_EQ(getVkInstanceProcAddrWithFallback<GfxstreamTestFunc>({}, instance), nullptr); |
| EXPECT_EQ(getVkInstanceProcAddrWithFallback<GfxstreamTestFunc>({nullptr, nullptr}, instance), |
| nullptr); |
| EXPECT_EQ(getVkInstanceProcAddrWithFallback<GfxstreamTestFunc>( |
| {vkGetInstanceProcAddrAlwaysNULL.AsStdFunction(), |
| vkGetInstanceProcAddrAlwaysNULL.AsStdFunction()}, |
| instance), |
| nullptr); |
| } |
| |
| TEST(getVkInstanceProcAddrWithFallbackTest, ShouldSkipNullVkGetInstanceProcAddr) { |
| VkInstance instance = reinterpret_cast<VkInstance>(0x1234'0000); |
| PFN_vkGfxstreamTestFunc validFp = reinterpret_cast<PFN_vkGfxstreamTestFunc>(0x4321'0000); |
| MockFunction<std::remove_pointer_t<PFN_vkGetInstanceProcAddr>> vkGetInstanceProcAddrMock; |
| |
| EXPECT_CALL(vkGetInstanceProcAddrMock, Call(instance, _)) |
| .WillRepeatedly(Return(reinterpret_cast<PFN_vkVoidFunction>(validFp))); |
| |
| EXPECT_EQ(getVkInstanceProcAddrWithFallback<GfxstreamTestFunc>( |
| {nullptr, vkGetInstanceProcAddrMock.AsStdFunction()}, instance), |
| validFp); |
| EXPECT_EQ(getVkInstanceProcAddrWithFallback<GfxstreamTestFunc>( |
| {vkGetInstanceProcAddrMock.AsStdFunction(), nullptr}, instance), |
| validFp); |
| } |
| |
| TEST(getVkInstanceProcAddrWithFallbackTest, ShouldSkipNullFpReturned) { |
| VkInstance instance = reinterpret_cast<VkInstance>(0x1234'0000); |
| PFN_vkGfxstreamTestFunc validFp = reinterpret_cast<PFN_vkGfxstreamTestFunc>(0x4321'0000); |
| MockFunction<std::remove_pointer_t<PFN_vkGetInstanceProcAddr>> vkGetInstanceProcAddrMock; |
| MockFunction<std::remove_pointer_t<PFN_vkGetInstanceProcAddr>> 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<PFN_vkVoidFunction>(validFp))); |
| EXPECT_CALL(vkGetInstanceProcAddrAlwaysNULL, Call(instance, _)).WillRepeatedly(Return(nullptr)); |
| |
| EXPECT_EQ(getVkInstanceProcAddrWithFallback<GfxstreamTestFunc>( |
| {vkGetInstanceProcAddrMock.AsStdFunction(), |
| vkGetInstanceProcAddrAlwaysNULL.AsStdFunction()}, |
| instance), |
| validFp); |
| EXPECT_EQ(getVkInstanceProcAddrWithFallback<GfxstreamTestFunc>( |
| {vkGetInstanceProcAddrAlwaysNULL.AsStdFunction(), |
| vkGetInstanceProcAddrMock.AsStdFunction()}, |
| instance), |
| validFp); |
| } |
| |
| TEST(getVkInstanceProcAddrWithFallbackTest, firstVkInstanceProcAddrShouldTakeThePriority) { |
| VkInstance instance = reinterpret_cast<VkInstance>(0x1234'0000); |
| PFN_vkGfxstreamTestFunc validFp1 = reinterpret_cast<PFN_vkGfxstreamTestFunc>(0x4321'0000); |
| PFN_vkGfxstreamTestFunc validFp2 = reinterpret_cast<PFN_vkGfxstreamTestFunc>(0x3421'0070); |
| MockFunction<std::remove_pointer_t<PFN_vkGetInstanceProcAddr>> vkGetInstanceProcAddrMock1; |
| MockFunction<std::remove_pointer_t<PFN_vkGetInstanceProcAddr>> vkGetInstanceProcAddrMock2; |
| |
| EXPECT_CALL(vkGetInstanceProcAddrMock1, Call(instance, _)) |
| .WillRepeatedly(Return(reinterpret_cast<PFN_vkVoidFunction>(validFp1))); |
| EXPECT_CALL(vkGetInstanceProcAddrMock2, Call(instance, _)) |
| .WillRepeatedly(Return(reinterpret_cast<PFN_vkVoidFunction>(validFp2))); |
| |
| EXPECT_EQ(getVkInstanceProcAddrWithFallback<GfxstreamTestFunc>( |
| {vkGetInstanceProcAddrMock1.AsStdFunction(), |
| vkGetInstanceProcAddrMock2.AsStdFunction()}, |
| instance), |
| validFp1); |
| } |
| |
| TEST(getVkInstanceProcAddrWithFallbackTest, firstNameShouldTakeThePriority) { |
| VkInstance instance = reinterpret_cast<VkInstance>(0x1234'0000); |
| PFN_vkGfxstreamTestFunc validFps[] = {reinterpret_cast<PFN_vkGfxstreamTestFunc>(0x4321'0000), |
| reinterpret_cast<PFN_vkGfxstreamTestFunc>(0x3421'0070), |
| reinterpret_cast<PFN_vkGfxstreamTestFunc>(0x2222'4321)}; |
| MockFunction<std::remove_pointer_t<PFN_vkGetInstanceProcAddr>> vkGetInstanceProcAddrMock; |
| |
| EXPECT_CALL(vkGetInstanceProcAddrMock, |
| Call(instance, StrEq(std::get<0>(vkGfxstreamTestFuncNames)))) |
| .WillRepeatedly(Return(reinterpret_cast<PFN_vkVoidFunction>(validFps[0]))); |
| ON_CALL(vkGetInstanceProcAddrMock, Call(instance, StrEq(std::get<1>(vkGfxstreamTestFuncNames)))) |
| .WillByDefault(Return(reinterpret_cast<PFN_vkVoidFunction>(validFps[1]))); |
| ON_CALL(vkGetInstanceProcAddrMock, Call(instance, StrEq(std::get<2>(vkGfxstreamTestFuncNames)))) |
| .WillByDefault(Return(reinterpret_cast<PFN_vkVoidFunction>(validFps[2]))); |
| |
| EXPECT_EQ(getVkInstanceProcAddrWithFallback<GfxstreamTestFunc>( |
| {vkGetInstanceProcAddrMock.AsStdFunction()}, instance), |
| validFps[0]); |
| } |
| |
| TEST(VkCheckCallbacksDeathTest, deviceLostCallbackShouldBeCalled) { |
| setVkCheckCallbacks(std::make_unique<VkCheckCallbacks>(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>(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>(VkCheckCallbacks{.onVkErrorDeviceLost = nullptr})); |
| EXPECT_EXIT(VK_CHECK(VK_ERROR_DEVICE_LOST), testing::ExitedWithCode(42), ""); |
| } |
| |
| template <typename T, class U = CrtpBase> |
| class ExampleCrtpClass1 : public U { |
| public: |
| void doCtrp1() { |
| T& self = static_cast<T&>(*this); |
| EXPECT_EQ(self.value, 42); |
| self.doCtrp1WasCalled = true; |
| } |
| }; |
| |
| template <typename T, class U = CrtpBase> |
| class ExampleCrtpClass2 : public U { |
| public: |
| void doCtrp2() { |
| T& self = static_cast<T&>(*this); |
| EXPECT_EQ(self.value, 42); |
| self.doCtrp2WasCalled = true; |
| } |
| }; |
| |
| template <typename T, class U = CrtpBase> |
| class ExampleCrtpClass3 : public U { |
| public: |
| void doCtrp3() { |
| T& self = static_cast<T&>(*this); |
| EXPECT_EQ(self.value, 42); |
| self.doCtrp3WasCalled = true; |
| } |
| }; |
| |
| struct MultiCrtpTestStruct |
| : MultiCrtp<MultiCrtpTestStruct, ExampleCrtpClass1, ExampleCrtpClass2, ExampleCrtpClass3> { |
| 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<VkBaseInStructure*>(&deviceCi); |
| ASSERT_EQ(base, reinterpret_cast<VkBaseInStructure*>(&deviceCi)); |
| base = base->pNext; |
| ASSERT_EQ(base, reinterpret_cast<VkBaseInStructure*>(&ycbcrFeature)); |
| base = base->pNext; |
| ASSERT_EQ(base, reinterpret_cast<VkBaseInStructure*>(&indexingFeatures)); |
| base = base->pNext; |
| ASSERT_EQ(base, reinterpret_cast<VkBaseInStructure*>(&physicalDeviceFeature)); |
| base = base->pNext; |
| ASSERT_EQ(base, nullptr); |
| } |
| |
| } // namespace |
| } // namespace vk_fn_info |
| } // namespace vk_util |
| } // namespace vk |
| } // namespace gfxstream |