/* * Copyright 2020 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. */ #define LOG_TAG "SurroundView3dSessionTests" #include "AnimationModule.h" #include "IOModule.h" #include "SurroundView3dSession.h" #include "VhalHandler.h" #include "mock-evs/MockEvsEnumerator.h" #include "mock-evs/MockSurroundViewCallback.h" #include #include #include #include #include #include #include namespace android { namespace hardware { namespace automotive { namespace sv { namespace V1_0 { namespace implementation { namespace { using ::android::sp; using ::android::hardware::hidl_memory; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; using ::android::hardware::automotive::sv::V1_0::OverlayMemoryDesc; using ::android::hidl::allocator::V1_0::IAllocator; using ::android::hidl::memory::V1_0::IMemory; const char* kSvConfigFilename = "vendor/etc/automotive/sv/sv_sample_config.xml"; // Sv 3D output height and width set by the config file. const int kSv3dWidth = 1920; const int kSv3dHeight = 1080; // Constants for overlays. const int kVertexByteSize = (3 * sizeof(float)) + 4; const int kIdByteSize = 2; class SurroundView3dSessionTests : public ::testing::Test { protected: // Setup sv3d session without vhal and animations. void SetupSv3dSession() { mFakeEvs = new MockEvsEnumerator(); mIoModule = new IOModule(kSvConfigFilename); EXPECT_EQ(mIoModule->initialize(), IOStatus::OK); mIoModule->getConfig(&mIoModuleConfig); mSv3dSession = new SurroundView3dSession(mFakeEvs, nullptr, nullptr, &mIoModuleConfig); EXPECT_TRUE(mSv3dSession->initialize()); mSv3dCallback = new MockSurroundViewCallback(mSv3dSession); vector views = { { .viewId = 0, .pose = { .rotation = {.x = 0, .y = 0, .z = 0, .w = 1.0f}, .translation = {.x = 0, .y = 0, .z = 0}, }, .horizontalFov = 90, }}; mSv3dSession->setViews(views); } // Setup sv3d session with vhal and animations. void SetupSv3dSessionVhalAnimation() { mFakeEvs = new MockEvsEnumerator(); mIoModule = new IOModule(kSvConfigFilename); EXPECT_EQ(mIoModule->initialize(), IOStatus::OK); mIoModule->getConfig(&mIoModuleConfig); mVhalHandler = new VhalHandler(); ASSERT_TRUE(mVhalHandler->initialize(VhalHandler::UpdateMethod::GET, 10)); mAnimationModule = new AnimationModule(mIoModuleConfig.carModelConfig.carModel.partsMap, mIoModuleConfig.carModelConfig.carModel.texturesMap, mIoModuleConfig.carModelConfig.animationConfig.animations); const std::vector animationPropertiesToRead = getAnimationPropertiesToRead(mIoModuleConfig.carModelConfig.animationConfig); ASSERT_TRUE(mVhalHandler->setPropertiesToRead(animationPropertiesToRead)); mSv3dSessionAnimations = new SurroundView3dSession(mFakeEvs, mVhalHandler, mAnimationModule, &mIoModuleConfig); EXPECT_TRUE(mSv3dSessionAnimations->initialize()); mSv3dCallbackAnimations = new MockSurroundViewCallback(mSv3dSessionAnimations); vector views = { // View 0 { .viewId = 0, .pose = { .rotation = {.x = 0, .y = 0, .z = 0, .w = 1.0f}, .translation = {.x = 0, .y = 0, .z = 0}, }, .horizontalFov = 90, }}; mSv3dSessionAnimations->setViews(views); } // Helper function to get list of vhal properties to read from car config file for animations. std::vector getAnimationPropertiesToRead(const AnimationConfig& animationConfig) { std::set propertiesSet; for (const auto& animation : animationConfig.animations) { for (const auto& opPair : animation.gammaOpsMap) { propertiesSet.insert(opPair.first); } for (const auto& opPair : animation.textureOpsMap) { propertiesSet.insert(opPair.first); } for (const auto& opPair : animation.rotationOpsMap) { propertiesSet.insert(opPair.first); } for (const auto& opPair : animation.translationOpsMap) { propertiesSet.insert(opPair.first); } } std::vector propertiesToRead; propertiesToRead.assign(propertiesSet.begin(), propertiesSet.end()); return propertiesToRead; } void TearDown() override { mSv3dSession = nullptr; mFakeEvs = nullptr; mSv3dCallback = nullptr; delete mVhalHandler; delete mAnimationModule; delete mIoModule; } sp mFakeEvs; IOModule* mIoModule; IOModuleConfig mIoModuleConfig; sp mSv3dSession; sp mSv3dCallback; VhalHandler* mVhalHandler; AnimationModule* mAnimationModule; sp mSv3dSessionAnimations; sp mSv3dCallbackAnimations; }; TEST_F(SurroundView3dSessionTests, startAndStop3dSession) { SetupSv3dSession(); EXPECT_EQ(mSv3dSession->startStream(mSv3dCallback), SvResult::OK); sleep(5); mSv3dSession->stopStream(); EXPECT_GT(mSv3dCallback->getReceivedFramesCount(), 0); } TEST_F(SurroundView3dSessionTests, get3dConfigSuccess) { SetupSv3dSession(); Sv3dConfig sv3dConfig; mSv3dSession->get3dConfig([&sv3dConfig](const Sv3dConfig& config) { sv3dConfig = config; }); EXPECT_EQ(sv3dConfig.width, kSv3dWidth); EXPECT_EQ(sv3dConfig.height, kSv3dHeight); EXPECT_EQ(sv3dConfig.carDetails, SvQuality::HIGH); } // Sets a different config and checks of the received config matches. TEST_F(SurroundView3dSessionTests, setAndGet3dConfigSuccess) { SetupSv3dSession(); Sv3dConfig sv3dConfigSet = {kSv3dWidth / 2, kSv3dHeight / 2, SvQuality::LOW}; EXPECT_EQ(mSv3dSession->set3dConfig(sv3dConfigSet), SvResult::OK); Sv3dConfig sv3dConfigReceived; mSv3dSession->get3dConfig( [&sv3dConfigReceived](const Sv3dConfig& config) { sv3dConfigReceived = config; }); EXPECT_EQ(sv3dConfigReceived.width, sv3dConfigSet.width); EXPECT_EQ(sv3dConfigReceived.height, sv3dConfigSet.height); EXPECT_EQ(sv3dConfigReceived.carDetails, sv3dConfigSet.carDetails); } // Projects center of each cameras and checks if valid projected point is received. TEST_F(SurroundView3dSessionTests, projectPoints3dSuccess) { SetupSv3dSession(); hidl_vec points2dCamera = { /*Center point*/ {.x = kSv3dWidth / 2, .y = kSv3dHeight / 2}}; std::vector cameraIds = {"/dev/video60", "/dev/video61", "/dev/video62", "/dev/video63"}; for (int i = 0; i < cameraIds.size(); i++) { mSv3dSession ->projectCameraPointsTo3dSurface(points2dCamera, cameraIds[i], [](const hidl_vec& projectedPoints) { EXPECT_TRUE(projectedPoints[0].isValid); }); } } std::pair> GetMappedSharedMemory(int bytesSize) { const auto nullResult = std::make_pair(hidl_memory(), nullptr); sp ashmemAllocator = IAllocator::getService("ashmem"); if (ashmemAllocator.get() == nullptr) { return nullResult; } // Allocate shared memory. hidl_memory hidlMemory; bool allocateSuccess = false; Return result = ashmemAllocator->allocate(bytesSize, [&](bool success, const hidl_memory& hidlMem) { if (!success) { return; } allocateSuccess = success; hidlMemory = hidlMem; }); // Check result of allocated memory. if (!result.isOk() || !allocateSuccess) { return nullResult; } // Map shared memory. sp pIMemory = mapMemory(hidlMemory); if (pIMemory.get() == nullptr) { return nullResult; } return std::make_pair(hidlMemory, pIMemory); } void SetIndexOfOverlaysMemory(const std::vector& overlaysMemDesc, sp pIMemory, int indexPosition, uint16_t indexValue) { // Count the number of vertices until the index. int totalVerticesCount = 0; for (int i = 0; i < indexPosition; i++) { totalVerticesCount += overlaysMemDesc[i].verticesCount; } const int indexBytePosition = (indexPosition * kIdByteSize) + (kVertexByteSize * totalVerticesCount); uint8_t* pSharedMemoryData = reinterpret_cast((void*)pIMemory->getPointer()); pSharedMemoryData += indexBytePosition; uint16_t* pIndex16bit = reinterpret_cast(pSharedMemoryData); // Modify shared memory. pIMemory->update(); *pIndex16bit = indexValue; pIMemory->commit(); } std::pair> GetSampleOverlaysData() { OverlaysData overlaysData; overlaysData.overlaysMemoryDesc.resize(2); int sharedMemBytesSize = 0; OverlayMemoryDesc overlayMemDesc1, overlayMemDesc2; overlayMemDesc1.id = 0; overlayMemDesc1.verticesCount = 6; overlayMemDesc1.overlayPrimitive = OverlayPrimitive::TRIANGLES; overlaysData.overlaysMemoryDesc[0] = overlayMemDesc1; sharedMemBytesSize += kIdByteSize + kVertexByteSize * overlayMemDesc1.verticesCount; overlayMemDesc2.id = 1; overlayMemDesc2.verticesCount = 4; overlayMemDesc2.overlayPrimitive = OverlayPrimitive::TRIANGLES_STRIP; overlaysData.overlaysMemoryDesc[1] = overlayMemDesc2; sharedMemBytesSize += kIdByteSize + kVertexByteSize * overlayMemDesc2.verticesCount; std::pair> sharedMem = GetMappedSharedMemory(sharedMemBytesSize); sp pIMemory = sharedMem.second; if (pIMemory.get() == nullptr) { return std::make_pair(OverlaysData(), nullptr); } // Get pointer to shared memory data and set all bytes to 0. uint8_t* pSharedMemoryData = reinterpret_cast((void*)pIMemory->getPointer()); pIMemory->update(); memset(pSharedMemoryData, 0, sharedMemBytesSize); pIMemory->commit(); std::vector overlaysDesc = {overlayMemDesc1, overlayMemDesc2}; // Set indexes in shared memory. SetIndexOfOverlaysMemory(overlaysDesc, pIMemory, 0, overlayMemDesc1.id); SetIndexOfOverlaysMemory(overlaysDesc, pIMemory, 1, overlayMemDesc2.id); overlaysData.overlaysMemoryDesc = overlaysDesc; overlaysData.overlaysMemory = sharedMem.first; return std::make_pair(overlaysData, pIMemory); } // Verifies a valid overlay can be updated while streaming. TEST_F(SurroundView3dSessionTests, updateOverlaysSuccess) { SetupSv3dSession(); std::pair> overlaysData = GetSampleOverlaysData(); ASSERT_NE(overlaysData.second, nullptr); EXPECT_EQ(mSv3dSession->startStream(mSv3dCallback), SvResult::OK); SvResult result = mSv3dSession->updateOverlays(overlaysData.first); mSv3dSession->stopStream(); EXPECT_EQ(result, SvResult::OK); } // Setup sv 3d sessin with vhal and animations and verify frames are received successfully. TEST_F(SurroundView3dSessionTests, vhalAnimationSuccess) { SetupSv3dSessionVhalAnimation(); EXPECT_EQ(mSv3dSessionAnimations->startStream(mSv3dCallbackAnimations), SvResult::OK); sleep(5); mSv3dSessionAnimations->stopStream(); EXPECT_GT(mSv3dCallbackAnimations->getReceivedFramesCount(), 0); } } // namespace } // namespace implementation } // namespace V1_0 } // namespace sv } // namespace automotive } // namespace hardware } // namespace android