| // |
| // Copyright © 2017 Arm Ltd. All rights reserved. |
| // SPDX-License-Identifier: MIT |
| // |
| #pragma once |
| |
| #ifndef LOG_TAG |
| #define LOG_TAG "ArmnnDriverTests" |
| #endif // LOG_TAG |
| |
| #include "../ArmnnDriver.hpp" |
| #include <iosfwd> |
| #include <boost/test/unit_test.hpp> |
| |
| #include <android/hidl/allocator/1.0/IAllocator.h> |
| |
| using ::android::hidl::allocator::V1_0::IAllocator; |
| |
| namespace android |
| { |
| namespace hardware |
| { |
| namespace neuralnetworks |
| { |
| namespace V1_0 |
| { |
| |
| std::ostream& operator<<(std::ostream& os, V1_0::ErrorStatus stat); |
| |
| } // namespace android::hardware::neuralnetworks::V1_0 |
| |
| #ifdef ARMNN_ANDROID_NN_V1_3 |
| namespace V1_3 |
| { |
| |
| std::ostream& operator<<(std::ostream& os, V1_3::ErrorStatus stat); |
| |
| } // namespace android::hardware::neuralnetworks::V1_3 |
| #endif |
| |
| } // namespace android::hardware::neuralnetworks |
| } // namespace android::hardware |
| } // namespace android |
| |
| namespace driverTestHelpers |
| { |
| |
| std::ostream& operator<<(std::ostream& os, V1_0::ErrorStatus stat); |
| |
| #ifdef ARMNN_ANDROID_NN_V1_3 |
| std::ostream& operator<<(std::ostream& os, V1_3::ErrorStatus stat); |
| #endif |
| |
| struct ExecutionCallback : public V1_0::IExecutionCallback |
| { |
| ExecutionCallback() : mNotified(false) {} |
| Return<void> notify(V1_0::ErrorStatus status) override; |
| /// wait until the callback has notified us that it is done |
| Return<void> wait(); |
| |
| private: |
| // use a mutex and a condition variable to wait for asynchronous callbacks |
| std::mutex mMutex; |
| std::condition_variable mCondition; |
| // and a flag, in case we are notified before the wait call |
| bool mNotified; |
| }; |
| |
| class PreparedModelCallback : public V1_0::IPreparedModelCallback |
| { |
| public: |
| PreparedModelCallback() |
| : m_ErrorStatus(V1_0::ErrorStatus::NONE) |
| , m_PreparedModel() |
| { } |
| ~PreparedModelCallback() override { } |
| |
| Return<void> notify(V1_0::ErrorStatus status, |
| const android::sp<V1_0::IPreparedModel>& preparedModel) override; |
| V1_0::ErrorStatus GetErrorStatus() { return m_ErrorStatus; } |
| android::sp<V1_0::IPreparedModel> GetPreparedModel() { return m_PreparedModel; } |
| |
| private: |
| V1_0::ErrorStatus m_ErrorStatus; |
| android::sp<V1_0::IPreparedModel> m_PreparedModel; |
| }; |
| |
| #if defined(ARMNN_ANDROID_NN_V1_2) || defined(ARMNN_ANDROID_NN_V1_3) |
| |
| class PreparedModelCallback_1_2 : public V1_2::IPreparedModelCallback |
| { |
| public: |
| PreparedModelCallback_1_2() |
| : m_ErrorStatus(V1_0::ErrorStatus::NONE) |
| , m_PreparedModel() |
| , m_PreparedModel_1_2() |
| { } |
| ~PreparedModelCallback_1_2() override { } |
| |
| Return<void> notify(V1_0::ErrorStatus status, const android::sp<V1_0::IPreparedModel>& preparedModel) override; |
| |
| Return<void> notify_1_2(V1_0::ErrorStatus status, const android::sp<V1_2::IPreparedModel>& preparedModel) override; |
| |
| V1_0::ErrorStatus GetErrorStatus() { return m_ErrorStatus; } |
| |
| android::sp<V1_0::IPreparedModel> GetPreparedModel() { return m_PreparedModel; } |
| |
| android::sp<V1_2::IPreparedModel> GetPreparedModel_1_2() { return m_PreparedModel_1_2; } |
| |
| private: |
| V1_0::ErrorStatus m_ErrorStatus; |
| android::sp<V1_0::IPreparedModel> m_PreparedModel; |
| android::sp<V1_2::IPreparedModel> m_PreparedModel_1_2; |
| }; |
| |
| #endif |
| |
| #ifdef ARMNN_ANDROID_NN_V1_3 |
| |
| class PreparedModelCallback_1_3 : public V1_3::IPreparedModelCallback |
| { |
| public: |
| PreparedModelCallback_1_3() |
| : m_1_0_ErrorStatus(V1_0::ErrorStatus::NONE) |
| , m_1_3_ErrorStatus(V1_3::ErrorStatus::NONE) |
| , m_PreparedModel() |
| , m_PreparedModel_1_2() |
| , m_PreparedModel_1_3() |
| { } |
| ~PreparedModelCallback_1_3() override { } |
| |
| Return<void> notify(V1_0::ErrorStatus status, const android::sp<V1_0::IPreparedModel>& preparedModel) override; |
| |
| Return<void> notify_1_2(V1_0::ErrorStatus status, const android::sp<V1_2::IPreparedModel>& preparedModel) override; |
| |
| Return<void> notify_1_3(V1_3::ErrorStatus status, const android::sp<V1_3::IPreparedModel>& preparedModel) override; |
| |
| V1_0::ErrorStatus GetErrorStatus() { return m_1_0_ErrorStatus; } |
| |
| V1_3::ErrorStatus Get_1_3_ErrorStatus() { return m_1_3_ErrorStatus; } |
| |
| android::sp<V1_0::IPreparedModel> GetPreparedModel() { return m_PreparedModel; } |
| |
| android::sp<V1_2::IPreparedModel> GetPreparedModel_1_2() { return m_PreparedModel_1_2; } |
| |
| android::sp<V1_3::IPreparedModel> GetPreparedModel_1_3() { return m_PreparedModel_1_3; } |
| |
| private: |
| V1_0::ErrorStatus m_1_0_ErrorStatus; |
| V1_3::ErrorStatus m_1_3_ErrorStatus; |
| android::sp<V1_0::IPreparedModel> m_PreparedModel; |
| android::sp<V1_2::IPreparedModel> m_PreparedModel_1_2; |
| android::sp<V1_3::IPreparedModel> m_PreparedModel_1_3; |
| }; |
| |
| #endif |
| |
| hidl_memory allocateSharedMemory(int64_t size); |
| |
| template<typename T> |
| android::sp<IMemory> AddPoolAndGetData(uint32_t size, V1_0::Request& request) |
| { |
| hidl_memory pool; |
| |
| android::sp<IAllocator> allocator = IAllocator::getService("ashmem"); |
| allocator->allocate(sizeof(T) * size, [&](bool success, const hidl_memory& mem) { |
| BOOST_TEST(success); |
| pool = mem; |
| }); |
| |
| request.pools.resize(request.pools.size() + 1); |
| request.pools[request.pools.size() - 1] = pool; |
| |
| android::sp<IMemory> mapped = mapMemory(pool); |
| mapped->update(); |
| return mapped; |
| } |
| |
| template<typename T> |
| void AddPoolAndSetData(uint32_t size, V1_0::Request& request, const T* data) |
| { |
| android::sp<IMemory> memory = AddPoolAndGetData<T>(size, request); |
| |
| T* dst = static_cast<T*>(static_cast<void*>(memory->getPointer())); |
| |
| memcpy(dst, data, size * sizeof(T)); |
| } |
| |
| template<typename HalPolicy, |
| typename HalModel = typename HalPolicy::Model, |
| typename HalOperand = typename HalPolicy::Operand> |
| void AddOperand(HalModel& model, const HalOperand& op) |
| { |
| model.operands.resize(model.operands.size() + 1); |
| model.operands[model.operands.size() - 1] = op; |
| } |
| |
| template<typename HalPolicy, typename HalModel = typename HalPolicy::Model> |
| void AddBoolOperand(HalModel& model, bool value, uint32_t numberOfConsumers = 1) |
| { |
| using HalOperand = typename HalPolicy::Operand; |
| using HalOperandType = typename HalPolicy::OperandType; |
| using HalOperandLifeTime = typename HalPolicy::OperandLifeTime; |
| |
| DataLocation location = {}; |
| location.offset = model.operandValues.size(); |
| location.length = sizeof(uint8_t); |
| |
| HalOperand op = {}; |
| op.type = HalOperandType::BOOL; |
| op.dimensions = hidl_vec<uint32_t>{}; |
| op.lifetime = HalOperandLifeTime::CONSTANT_COPY; |
| op.location = location; |
| op.numberOfConsumers = numberOfConsumers; |
| |
| model.operandValues.resize(model.operandValues.size() + location.length); |
| *reinterpret_cast<uint8_t*>(&model.operandValues[location.offset]) = static_cast<uint8_t>(value); |
| |
| AddOperand<HalModel>(model, op); |
| } |
| |
| template<typename T> |
| OperandType TypeToOperandType(); |
| |
| template<> |
| OperandType TypeToOperandType<float>(); |
| |
| template<> |
| OperandType TypeToOperandType<int32_t>(); |
| |
| template<typename HalPolicy, |
| typename HalModel = typename HalPolicy::Model, |
| typename HalOperandType = typename HalPolicy::OperandType> |
| void AddInputOperand(HalModel& model, |
| const hidl_vec<uint32_t>& dimensions, |
| HalOperandType operandType = HalOperandType::TENSOR_FLOAT32, |
| double scale = 0.f, |
| int offset = 0, |
| uint32_t numberOfConsumers = 1) |
| { |
| using HalOperand = typename HalPolicy::Operand; |
| using HalOperandLifeTime = typename HalPolicy::OperandLifeTime; |
| |
| HalOperand op = {}; |
| op.type = operandType; |
| op.scale = scale; |
| op.zeroPoint = offset; |
| op.dimensions = dimensions; |
| op.lifetime = HalOperandLifeTime::MODEL_INPUT; |
| op.numberOfConsumers = numberOfConsumers; |
| |
| AddOperand<HalPolicy>(model, op); |
| |
| model.inputIndexes.resize(model.inputIndexes.size() + 1); |
| model.inputIndexes[model.inputIndexes.size() - 1] = model.operands.size() - 1; |
| } |
| |
| template<typename HalPolicy, |
| typename HalModel = typename HalPolicy::Model, |
| typename HalOperandType = typename HalPolicy::OperandType> |
| void AddOutputOperand(HalModel& model, |
| const hidl_vec<uint32_t>& dimensions, |
| HalOperandType operandType = HalOperandType::TENSOR_FLOAT32, |
| double scale = 0.f, |
| int offset = 0, |
| uint32_t numberOfConsumers = 0) |
| { |
| using HalOperand = typename HalPolicy::Operand; |
| using HalOperandLifeTime = typename HalPolicy::OperandLifeTime; |
| |
| HalOperand op = {}; |
| op.type = operandType; |
| op.scale = scale; |
| op.zeroPoint = offset; |
| op.dimensions = dimensions; |
| op.lifetime = HalOperandLifeTime::MODEL_OUTPUT; |
| op.numberOfConsumers = numberOfConsumers; |
| |
| AddOperand<HalPolicy>(model, op); |
| |
| model.outputIndexes.resize(model.outputIndexes.size() + 1); |
| model.outputIndexes[model.outputIndexes.size() - 1] = model.operands.size() - 1; |
| } |
| |
| android::sp<V1_0::IPreparedModel> PrepareModelWithStatus(const V1_0::Model& model, |
| armnn_driver::ArmnnDriver& driver, |
| V1_0::ErrorStatus& prepareStatus, |
| V1_0::ErrorStatus expectedStatus = V1_0::ErrorStatus::NONE); |
| |
| #if defined(ARMNN_ANDROID_NN_V1_1) || defined(ARMNN_ANDROID_NN_V1_2) || defined(ARMNN_ANDROID_NN_V1_3) |
| |
| android::sp<V1_0::IPreparedModel> PrepareModelWithStatus(const V1_1::Model& model, |
| armnn_driver::ArmnnDriver& driver, |
| V1_0::ErrorStatus& prepareStatus, |
| V1_0::ErrorStatus expectedStatus = V1_0::ErrorStatus::NONE); |
| |
| #endif |
| |
| template<typename HalModel> |
| android::sp<V1_0::IPreparedModel> PrepareModel(const HalModel& model, |
| armnn_driver::ArmnnDriver& driver) |
| { |
| V1_0::ErrorStatus prepareStatus = V1_0::ErrorStatus::NONE; |
| return PrepareModelWithStatus(model, driver, prepareStatus); |
| } |
| |
| #if defined(ARMNN_ANDROID_NN_V1_2) || defined(ARMNN_ANDROID_NN_V1_3) |
| |
| android::sp<V1_2::IPreparedModel> PrepareModelWithStatus_1_2(const armnn_driver::hal_1_2::HalPolicy::Model& model, |
| armnn_driver::ArmnnDriver& driver, |
| V1_0::ErrorStatus& prepareStatus, |
| V1_0::ErrorStatus expectedStatus = V1_0::ErrorStatus::NONE); |
| |
| template<typename HalModel> |
| android::sp<V1_2::IPreparedModel> PrepareModel_1_2(const HalModel& model, |
| armnn_driver::ArmnnDriver& driver) |
| { |
| V1_0::ErrorStatus prepareStatus = V1_0::ErrorStatus::NONE; |
| return PrepareModelWithStatus_1_2(model, driver, prepareStatus); |
| } |
| |
| #endif |
| |
| #ifdef ARMNN_ANDROID_NN_V1_3 |
| |
| template<typename HalPolicy> |
| void AddOperand(armnn_driver::hal_1_3::HalPolicy::Model& model, |
| const armnn_driver::hal_1_3::HalPolicy::Operand& op) |
| { |
| model.main.operands.resize(model.main.operands.size() + 1); |
| model.main.operands[model.main.operands.size() - 1] = op; |
| } |
| |
| template<typename HalPolicy> |
| void AddInputOperand(armnn_driver::hal_1_3::HalPolicy::Model& model, |
| const hidl_vec<uint32_t>& dimensions, |
| armnn_driver::hal_1_3::HalPolicy::OperandType operandType = |
| armnn_driver::hal_1_3::HalPolicy::OperandType::TENSOR_FLOAT32, |
| double scale = 0.f, |
| int offset = 0, |
| uint32_t numberOfConsumers = 1) |
| { |
| using HalOperand = typename armnn_driver::hal_1_3::HalPolicy::Operand; |
| using HalOperandLifeTime = typename armnn_driver::hal_1_3::HalPolicy::OperandLifeTime; |
| |
| HalOperand op = {}; |
| op.type = operandType; |
| op.scale = scale; |
| op.zeroPoint = offset; |
| op.dimensions = dimensions; |
| op.lifetime = HalOperandLifeTime::SUBGRAPH_INPUT; |
| op.numberOfConsumers = numberOfConsumers; |
| |
| AddOperand<HalPolicy>(model, op); |
| |
| model.main.inputIndexes.resize(model.main.inputIndexes.size() + 1); |
| model.main.inputIndexes[model.main.inputIndexes.size() - 1] = model.main.operands.size() - 1; |
| } |
| |
| template<typename HalPolicy> |
| void AddOutputOperand(armnn_driver::hal_1_3::HalPolicy::Model& model, |
| const hidl_vec<uint32_t>& dimensions, |
| armnn_driver::hal_1_3::HalPolicy::OperandType operandType = |
| armnn_driver::hal_1_3::HalPolicy::OperandType::TENSOR_FLOAT32, |
| double scale = 0.f, |
| int offset = 0, |
| uint32_t numberOfConsumers = 0) |
| { |
| using HalOperand = typename armnn_driver::hal_1_3::HalPolicy::Operand; |
| using HalOperandLifeTime = typename armnn_driver::hal_1_3::HalPolicy::OperandLifeTime; |
| |
| HalOperand op = {}; |
| op.type = operandType; |
| op.scale = scale; |
| op.zeroPoint = offset; |
| op.dimensions = dimensions; |
| op.lifetime = HalOperandLifeTime::SUBGRAPH_OUTPUT; |
| op.numberOfConsumers = numberOfConsumers; |
| |
| AddOperand<HalPolicy>(model, op); |
| |
| model.main.outputIndexes.resize(model.main.outputIndexes.size() + 1); |
| model.main.outputIndexes[model.main.outputIndexes.size() - 1] = model.main.operands.size() - 1; |
| } |
| |
| android::sp<V1_3::IPreparedModel> PrepareModelWithStatus_1_3(const armnn_driver::hal_1_3::HalPolicy::Model& model, |
| armnn_driver::ArmnnDriver& driver, |
| V1_3::ErrorStatus& prepareStatus, |
| V1_3::Priority priority = V1_3::Priority::LOW); |
| |
| template<typename HalModel> |
| android::sp<V1_3::IPreparedModel> PrepareModel_1_3(const HalModel& model, |
| armnn_driver::ArmnnDriver& driver) |
| { |
| V1_3::ErrorStatus prepareStatus = V1_3::ErrorStatus::NONE; |
| return PrepareModelWithStatus_1_3(model, driver, prepareStatus); |
| } |
| |
| #endif |
| |
| template<typename HalPolicy, |
| typename T, |
| typename HalModel = typename HalPolicy::Model, |
| typename HalOperandType = typename HalPolicy::OperandType, |
| typename HalOperandLifeTime = typename HalPolicy::OperandLifeTime> |
| void AddTensorOperand(HalModel& model, |
| const hidl_vec<uint32_t>& dimensions, |
| const T* values, |
| HalOperandType operandType = HalOperandType::TENSOR_FLOAT32, |
| HalOperandLifeTime operandLifeTime = V1_0::OperandLifeTime::CONSTANT_COPY, |
| double scale = 0.f, |
| int offset = 0, |
| uint32_t numberOfConsumers = 1) |
| { |
| using HalOperand = typename HalPolicy::Operand; |
| |
| uint32_t totalElements = 1; |
| for (uint32_t dim : dimensions) |
| { |
| totalElements *= dim; |
| } |
| |
| DataLocation location = {}; |
| location.length = totalElements * sizeof(T); |
| |
| if(operandLifeTime == HalOperandLifeTime::CONSTANT_COPY) |
| { |
| location.offset = model.operandValues.size(); |
| } |
| |
| HalOperand op = {}; |
| op.type = operandType; |
| op.dimensions = dimensions; |
| op.scale = scale; |
| op.zeroPoint = offset; |
| op.lifetime = HalOperandLifeTime::CONSTANT_COPY; |
| op.location = location; |
| op.numberOfConsumers = numberOfConsumers; |
| |
| model.operandValues.resize(model.operandValues.size() + location.length); |
| for (uint32_t i = 0; i < totalElements; i++) |
| { |
| *(reinterpret_cast<T*>(&model.operandValues[location.offset]) + i) = values[i]; |
| } |
| |
| AddOperand<HalPolicy>(model, op); |
| } |
| |
| template<typename HalPolicy, |
| typename T, |
| typename HalModel = typename HalPolicy::Model, |
| typename HalOperandType = typename HalPolicy::OperandType, |
| typename HalOperandLifeTime = typename HalPolicy::OperandLifeTime> |
| void AddTensorOperand(HalModel& model, |
| const hidl_vec<uint32_t>& dimensions, |
| const std::vector<T>& values, |
| HalOperandType operandType = HalPolicy::OperandType::TENSOR_FLOAT32, |
| HalOperandLifeTime operandLifeTime = V1_0::OperandLifeTime::CONSTANT_COPY, |
| double scale = 0.f, |
| int offset = 0, |
| uint32_t numberOfConsumers = 1) |
| { |
| AddTensorOperand<HalPolicy, T>(model, |
| dimensions, |
| values.data(), |
| operandType, |
| operandLifeTime, |
| scale, |
| offset, |
| numberOfConsumers); |
| } |
| |
| template<typename HalPolicy, typename HalModel = typename HalPolicy::Model> |
| void AddIntOperand(HalModel& model, int32_t value, uint32_t numberOfConsumers = 1) |
| { |
| using HalOperand = typename HalPolicy::Operand; |
| using HalOperandType = typename HalPolicy::OperandType; |
| using HalOperandLifeTime = typename HalPolicy::OperandLifeTime; |
| |
| DataLocation location = {}; |
| location.offset = model.operandValues.size(); |
| location.length = sizeof(int32_t); |
| |
| HalOperand op = {}; |
| op.type = HalOperandType::INT32; |
| op.dimensions = hidl_vec<uint32_t>{}; |
| op.lifetime = HalOperandLifeTime::CONSTANT_COPY; |
| op.location = location; |
| op.numberOfConsumers = numberOfConsumers; |
| |
| model.operandValues.resize(model.operandValues.size() + location.length); |
| *reinterpret_cast<int32_t*>(&model.operandValues[location.offset]) = value; |
| |
| AddOperand<HalPolicy>(model, op); |
| } |
| |
| template<typename HalPolicy, typename HalModel = typename HalPolicy::Model> |
| void AddFloatOperand(HalModel& model, |
| float value, |
| uint32_t numberOfConsumers = 1) |
| { |
| using HalOperand = typename HalPolicy::Operand; |
| using HalOperandType = typename HalPolicy::OperandType; |
| using HalOperandLifeTime = typename HalPolicy::OperandLifeTime; |
| |
| DataLocation location = {}; |
| location.offset = model.operandValues.size(); |
| location.length = sizeof(float); |
| |
| HalOperand op = {}; |
| op.type = HalOperandType::FLOAT32; |
| op.dimensions = hidl_vec<uint32_t>{}; |
| op.lifetime = HalOperandLifeTime::CONSTANT_COPY; |
| op.location = location; |
| op.numberOfConsumers = numberOfConsumers; |
| |
| model.operandValues.resize(model.operandValues.size() + location.length); |
| *reinterpret_cast<float*>(&model.operandValues[location.offset]) = value; |
| |
| AddOperand<HalPolicy>(model, op); |
| } |
| |
| V1_0::ErrorStatus Execute(android::sp<V1_0::IPreparedModel> preparedModel, |
| const V1_0::Request& request, |
| V1_0::ErrorStatus expectedStatus = V1_0::ErrorStatus::NONE); |
| |
| android::sp<ExecutionCallback> ExecuteNoWait(android::sp<V1_0::IPreparedModel> preparedModel, |
| const V1_0::Request& request); |
| |
| } // namespace driverTestHelpers |