blob: afe8f07da0014054bb3b51aa6c86bc41d295a772 [file] [log] [blame]
/*
* 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 <android-base/logging.h>
#include <android/hardware/automotive/vehicle/2.0/IVehicle.h>
#include <android/hidl/allocator/1.0/IAllocator.h>
#include <android/hidl/memory/1.0/IMemory.h>
#include <gtest/gtest.h>
#include <hidlmemory/mapping.h>
#include <time.h>
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<View3d> 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<uint64_t> 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<View3d> 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<uint64_t> getAnimationPropertiesToRead(const AnimationConfig& animationConfig) {
std::set<uint64_t> 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<uint64_t> 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<IEvsEnumerator> mFakeEvs;
IOModule* mIoModule;
IOModuleConfig mIoModuleConfig;
sp<SurroundView3dSession> mSv3dSession;
sp<MockSurroundViewCallback> mSv3dCallback;
VhalHandler* mVhalHandler;
AnimationModule* mAnimationModule;
sp<SurroundView3dSession> mSv3dSessionAnimations;
sp<MockSurroundViewCallback> 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<Point2dInt> points2dCamera = {
/*Center point*/ {.x = kSv3dWidth / 2, .y = kSv3dHeight / 2}};
std::vector<hidl_string> 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<Point3dFloat>& projectedPoints) {
EXPECT_TRUE(projectedPoints[0].isValid);
});
}
}
std::pair<hidl_memory, sp<IMemory>> GetMappedSharedMemory(int bytesSize) {
const auto nullResult = std::make_pair(hidl_memory(), nullptr);
sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem");
if (ashmemAllocator.get() == nullptr) {
return nullResult;
}
// Allocate shared memory.
hidl_memory hidlMemory;
bool allocateSuccess = false;
Return<void> 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<IMemory> pIMemory = mapMemory(hidlMemory);
if (pIMemory.get() == nullptr) {
return nullResult;
}
return std::make_pair(hidlMemory, pIMemory);
}
void SetIndexOfOverlaysMemory(const std::vector<OverlayMemoryDesc>& overlaysMemDesc,
sp<IMemory> 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<uint8_t*>((void*)pIMemory->getPointer());
pSharedMemoryData += indexBytePosition;
uint16_t* pIndex16bit = reinterpret_cast<uint16_t*>(pSharedMemoryData);
// Modify shared memory.
pIMemory->update();
*pIndex16bit = indexValue;
pIMemory->commit();
}
std::pair<OverlaysData, sp<IMemory>> 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<hidl_memory, sp<IMemory>> sharedMem = GetMappedSharedMemory(sharedMemBytesSize);
sp<IMemory> 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<uint8_t*>((void*)pIMemory->getPointer());
pIMemory->update();
memset(pSharedMemoryData, 0, sharedMemBytesSize);
pIMemory->commit();
std::vector<OverlayMemoryDesc> 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, sp<IMemory>> 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