diff options
Diffstat (limited to 'neuralnetworks/aidl/utils/src')
-rw-r--r-- | neuralnetworks/aidl/utils/src/Buffer.cpp | 1 | ||||
-rw-r--r-- | neuralnetworks/aidl/utils/src/BufferTracker.cpp | 227 | ||||
-rw-r--r-- | neuralnetworks/aidl/utils/src/Burst.cpp | 91 | ||||
-rw-r--r-- | neuralnetworks/aidl/utils/src/Callbacks.cpp | 12 | ||||
-rw-r--r-- | neuralnetworks/aidl/utils/src/Conversions.cpp | 515 | ||||
-rw-r--r-- | neuralnetworks/aidl/utils/src/Device.cpp | 40 | ||||
-rw-r--r-- | neuralnetworks/aidl/utils/src/Execution.cpp | 56 | ||||
-rw-r--r-- | neuralnetworks/aidl/utils/src/HalUtils.cpp | 72 | ||||
-rw-r--r-- | neuralnetworks/aidl/utils/src/InvalidDevice.cpp | 26 | ||||
-rw-r--r-- | neuralnetworks/aidl/utils/src/PreparedModel.cpp | 260 | ||||
-rw-r--r-- | neuralnetworks/aidl/utils/src/ProtectCallback.cpp | 9 | ||||
-rw-r--r-- | neuralnetworks/aidl/utils/src/Service.cpp | 35 | ||||
-rw-r--r-- | neuralnetworks/aidl/utils/src/Utils.cpp | 26 | ||||
-rw-r--r-- | neuralnetworks/aidl/utils/src/ValidateHal.cpp | 136 |
14 files changed, 1154 insertions, 352 deletions
diff --git a/neuralnetworks/aidl/utils/src/Buffer.cpp b/neuralnetworks/aidl/utils/src/Buffer.cpp index c729a688d1..9af3c9e3ab 100644 --- a/neuralnetworks/aidl/utils/src/Buffer.cpp +++ b/neuralnetworks/aidl/utils/src/Buffer.cpp @@ -22,7 +22,6 @@ #include "Conversions.h" #include "Utils.h" -#include "nnapi/hal/aidl/Conversions.h" #include <memory> #include <utility> diff --git a/neuralnetworks/aidl/utils/src/BufferTracker.cpp b/neuralnetworks/aidl/utils/src/BufferTracker.cpp new file mode 100644 index 0000000000..94dc89171e --- /dev/null +++ b/neuralnetworks/aidl/utils/src/BufferTracker.cpp @@ -0,0 +1,227 @@ +/* + * Copyright (C) 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. + */ + +#include "BufferTracker.h" + +#include "HalInterfaces.h" + +#include <android-base/macros.h> +#include <nnapi/TypeUtils.h> + +#include <memory> +#include <mutex> +#include <set> +#include <stack> +#include <utility> +#include <vector> + +namespace android::nn { + +std::shared_ptr<AidlManagedBuffer> AidlManagedBuffer::create( + uint32_t size, std::set<AidlHalPreparedModelRole> roles, const Operand& operand) { + std::unique_ptr<uint8_t[]> buffer(new (std::nothrow) uint8_t[size]); + if (buffer == nullptr) { + return nullptr; + } + if (isExtension(operand.type)) { + LOG(ERROR) << "AidlManagedBuffer cannot handle extension operands."; + return nullptr; + } + return std::make_shared<AidlManagedBuffer>(std::move(buffer), size, std::move(roles), operand); +} + +AidlManagedBuffer::AidlManagedBuffer(std::unique_ptr<uint8_t[]> buffer, uint32_t size, + std::set<AidlHalPreparedModelRole> roles, + const Operand& operand) + : kBuffer(std::move(buffer)), + kSize(size), + kRoles(std::move(roles)), + kOperandType(operand.type), + kInitialDimensions(operand.dimensions), + mUpdatedDimensions(operand.dimensions) { + CHECK(!isExtension(kOperandType)); +} + +ErrorStatus AidlManagedBuffer::validateRequest( + uint32_t poolIndex, const Request& request, + const aidl_hal::IPreparedModel* preparedModel) const { + CHECK_LT(poolIndex, request.pools.size()); + CHECK(std::holds_alternative<Request::MemoryDomainToken>(request.pools[poolIndex])); + std::lock_guard<std::mutex> guard(mMutex); + + bool usedAsInput = false, usedAsOutput = false; + for (uint32_t i = 0; i < request.inputs.size(); i++) { + if (request.inputs[i].lifetime != Request::Argument::LifeTime::POOL) continue; + if (request.inputs[i].location.poolIndex != poolIndex) continue; + // Validate if the input role is specified during allocation. + if (kRoles.count({preparedModel, IOType::INPUT, i}) == 0) { + LOG(ERROR) << "AidlManagedBuffer::validateRequest -- invalid buffer role."; + return ErrorStatus::INVALID_ARGUMENT; + } + if (!mInitialized) { + LOG(ERROR) + << "AidlManagedBuffer::validateRequest -- using uninitialized buffer as input " + "request."; + return ErrorStatus::GENERAL_FAILURE; + } + auto combined = combineDimensions(mUpdatedDimensions, request.inputs[i].dimensions); + if (!combined.has_value()) { + LOG(ERROR) << "AidlManagedBuffer::validateRequest -- incompatible dimensions (" + << toString(mUpdatedDimensions) << " vs " + << toString(request.inputs[i].dimensions) << ")"; + return ErrorStatus::INVALID_ARGUMENT; + } + usedAsInput = true; + } + for (uint32_t i = 0; i < request.outputs.size(); i++) { + if (request.outputs[i].lifetime != Request::Argument::LifeTime::POOL) continue; + if (request.outputs[i].location.poolIndex != poolIndex) continue; + if (usedAsInput || usedAsOutput) { + LOG(ERROR) << "AidlManagedBuffer::validateRequest -- using the same device memory for " + "input/output or multiple outputs"; + return ErrorStatus::INVALID_ARGUMENT; + } + // Validate if the output role is specified during allocation. + if (kRoles.count({preparedModel, IOType::OUTPUT, i}) == 0) { + LOG(ERROR) << "AidlManagedBuffer::validateRequest -- invalid buffer role."; + return ErrorStatus::INVALID_ARGUMENT; + } + auto combined = combineDimensions(kInitialDimensions, request.outputs[i].dimensions); + if (!combined.has_value()) { + LOG(ERROR) << "AidlManagedBuffer::validateRequest -- incompatible dimensions (" + << toString(kInitialDimensions) << " vs " + << toString(request.outputs[i].dimensions) << ")"; + return ErrorStatus::INVALID_ARGUMENT; + } + usedAsOutput = true; + } + return ErrorStatus::NONE; +} + +ErrorStatus AidlManagedBuffer::validateCopyFrom(const std::vector<uint32_t>& dimensions, + uint32_t size) const { + if (size != kSize) { + LOG(ERROR) << "AidlManagedBuffer::validateCopyFrom -- invalid memory size: " << kSize + << " vs " << size; + return ErrorStatus::INVALID_ARGUMENT; + } + + if (isNonExtensionScalar(kOperandType)) { + if (!dimensions.empty()) { + LOG(ERROR) << "AidlManagedBuffer::validateCopyFrom -- invalid dimensions for scalar " + "operand: " + << toString(dimensions); + return ErrorStatus::INVALID_ARGUMENT; + } + return ErrorStatus::NONE; + } + + if (dimensions.empty()) { + if (tensorHasUnspecifiedDimensions(kOperandType, kInitialDimensions)) { + LOG(ERROR) << "AidlManagedBuffer::validateCopyFrom -- the initial dimensions are not " + "fully " + "specified and no dimension update is provided: " + << toString(kInitialDimensions); + return ErrorStatus::INVALID_ARGUMENT; + } + } else { + if (tensorHasUnspecifiedDimensions(kOperandType, dimensions)) { + LOG(ERROR) << "AidlManagedBuffer::validateCopyFrom -- the updated dimensions are not " + "fully " + "specified: " + << toString(dimensions); + return ErrorStatus::INVALID_ARGUMENT; + } + } + + const auto combined = combineDimensions(kInitialDimensions, dimensions); + if (!combined.has_value()) { + LOG(ERROR) << "AidlManagedBuffer::validateCopyFrom -- incompatible dimensions (" + << toString(kInitialDimensions) << " vs " << toString(dimensions) << ")"; + return ErrorStatus::INVALID_ARGUMENT; + } + return ErrorStatus::NONE; +} + +ErrorStatus AidlManagedBuffer::validateCopyTo(uint32_t size) const { + if (size != kSize) { + LOG(ERROR) << "AidlManagedBuffer::validateCopyTo -- invalid memory size: " << kSize + << " vs " << size; + return ErrorStatus::INVALID_ARGUMENT; + } + std::lock_guard<std::mutex> guard(mMutex); + if (!mInitialized) { + LOG(ERROR) << "AidlManagedBuffer::validateCopyTo -- using uninitialized buffer as source."; + return ErrorStatus::GENERAL_FAILURE; + } + return ErrorStatus::NONE; +} + +bool AidlManagedBuffer::updateDimensions(const std::vector<uint32_t>& dimensions) { + auto combined = combineDimensions(kInitialDimensions, dimensions); + if (!combined.has_value()) { + LOG(ERROR) << "AidlManagedBuffer::updateDimensions -- incompatible dimensions (" + << toString(kInitialDimensions) << " vs " << toString(dimensions) << ")"; + return false; + } + std::lock_guard<std::mutex> guard(mMutex); + mUpdatedDimensions = std::move(combined).value(); + return true; +} + +void AidlManagedBuffer::setInitialized(bool initialized) { + std::lock_guard<std::mutex> guard(mMutex); + mInitialized = initialized; +} + +std::unique_ptr<AidlBufferTracker::Token> AidlBufferTracker::add( + std::shared_ptr<AidlManagedBuffer> buffer) { + if (buffer == nullptr) { + return nullptr; + } + std::lock_guard<std::mutex> guard(mMutex); + uint32_t token = 0; + if (mFreeTokens.empty()) { + token = mTokenToBuffers.size(); + mTokenToBuffers.push_back(std::move(buffer)); + } else { + token = mFreeTokens.top(); + mFreeTokens.pop(); + mTokenToBuffers[token] = std::move(buffer); + } + VLOG(MEMORY) << "AidlBufferTracker::add -- new token = " << token; + return std::make_unique<Token>(token, shared_from_this()); +} + +std::shared_ptr<AidlManagedBuffer> AidlBufferTracker::get(uint32_t token) const { + std::lock_guard<std::mutex> guard(mMutex); + if (mTokenToBuffers.size() <= token || mTokenToBuffers[token] == nullptr) { + LOG(ERROR) << "AidlBufferTracker::get -- unknown token " << token; + return nullptr; + } + return mTokenToBuffers[token]; +} + +void AidlBufferTracker::free(uint32_t token) { + std::lock_guard<std::mutex> guard(mMutex); + CHECK_LT(token, mTokenToBuffers.size()); + CHECK(mTokenToBuffers[token] != nullptr); + VLOG(MEMORY) << "AidlBufferTracker::free -- release token = " << token; + mTokenToBuffers[token] = nullptr; + mFreeTokens.push(token); +} + +} // namespace android::nn diff --git a/neuralnetworks/aidl/utils/src/Burst.cpp b/neuralnetworks/aidl/utils/src/Burst.cpp index 800ac32944..6c7aa882e8 100644 --- a/neuralnetworks/aidl/utils/src/Burst.cpp +++ b/neuralnetworks/aidl/utils/src/Burst.cpp @@ -26,7 +26,6 @@ #include <nnapi/Result.h> #include <nnapi/TypeUtils.h> #include <nnapi/Types.h> -#include <nnapi/hal/HandleError.h> #include <memory> #include <mutex> @@ -44,12 +43,16 @@ class BurstExecution final : public nn::IExecution, static nn::GeneralResult<std::shared_ptr<const BurstExecution>> create( std::shared_ptr<const Burst> burst, Request request, std::vector<int64_t> memoryIdentifierTokens, bool measure, int64_t loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix, hal::utils::RequestRelocation relocation, std::vector<Burst::OptionalCacheHold> cacheHolds); BurstExecution(PrivateConstructorTag tag, std::shared_ptr<const Burst> burst, Request request, std::vector<int64_t> memoryIdentifierTokens, bool measure, - int64_t loopTimeoutDuration, hal::utils::RequestRelocation relocation, + int64_t loopTimeoutDuration, const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix, + hal::utils::RequestRelocation relocation, std::vector<Burst::OptionalCacheHold> cacheHolds); nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute( @@ -65,6 +68,8 @@ class BurstExecution final : public nn::IExecution, const std::vector<int64_t> kMemoryIdentifierTokens; const bool kMeasure; const int64_t kLoopTimeoutDuration; + const std::vector<nn::TokenValuePair> kHints; + const std::vector<nn::ExtensionNameAndPrefix> kExtensionNameToPrefix; const hal::utils::RequestRelocation kRelocation; const std::vector<Burst::OptionalCacheHold> kCacheHolds; }; @@ -150,17 +155,20 @@ void Burst::MemoryCache::tryFreeMemory(const nn::SharedMemory& memory, int64_t i } nn::GeneralResult<std::shared_ptr<const Burst>> Burst::create( - std::shared_ptr<aidl_hal::IBurst> burst) { + std::shared_ptr<aidl_hal::IBurst> burst, nn::Version featureLevel) { if (burst == nullptr) { return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "aidl_hal::utils::Burst::create must have non-null burst"; } - return std::make_shared<const Burst>(PrivateConstructorTag{}, std::move(burst)); + return std::make_shared<const Burst>(PrivateConstructorTag{}, std::move(burst), featureLevel); } -Burst::Burst(PrivateConstructorTag /*tag*/, std::shared_ptr<aidl_hal::IBurst> burst) - : kBurst(std::move(burst)), kMemoryCache(std::make_shared<MemoryCache>(kBurst)) { +Burst::Burst(PrivateConstructorTag /*tag*/, std::shared_ptr<aidl_hal::IBurst> burst, + nn::Version featureLevel) + : kBurst(std::move(burst)), + kMemoryCache(std::make_shared<MemoryCache>(kBurst)), + kFeatureLevel(featureLevel) { CHECK(kBurst != nullptr); } @@ -171,21 +179,20 @@ Burst::OptionalCacheHold Burst::cacheMemory(const nn::SharedMemory& memory) cons nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst::execute( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration) const { + const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { // Ensure that request is ready for IPC. std::optional<nn::Request> maybeRequestInShared; hal::utils::RequestRelocation relocation; - const nn::Request& requestInShared = - NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared( - &request, nn::kDefaultRequestMemoryAlignment, nn::kDefaultRequestMemoryPadding, - &maybeRequestInShared, &relocation))); + const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared( + &request, nn::kDefaultRequestMemoryAlignment, nn::kDefaultRequestMemoryPadding, + &maybeRequestInShared, &relocation)); - const auto aidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared))); - const auto aidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure))); - const auto aidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline))); - const auto aidlLoopTimeoutDuration = - NN_TRY(hal::utils::makeExecutionFailure(convert(loopTimeoutDuration))); + const auto aidlRequest = NN_TRY(convert(requestInShared)); + const auto aidlMeasure = NN_TRY(convert(measure)); + const auto aidlDeadline = NN_TRY(convert(deadline)); + const auto aidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration)); std::vector<int64_t> memoryIdentifierTokens; std::vector<OptionalCacheHold> holds; @@ -203,14 +210,14 @@ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst:: memoryIdentifierTokens.push_back(-1); } CHECK_EQ(requestInShared.pools.size(), memoryIdentifierTokens.size()); - return executeInternal(aidlRequest, memoryIdentifierTokens, aidlMeasure, aidlDeadline, - aidlLoopTimeoutDuration, relocation); + aidlLoopTimeoutDuration, hints, extensionNameToPrefix, relocation); } nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst::executeInternal( const Request& request, const std::vector<int64_t>& memoryIdentifierTokens, bool measure, - int64_t deadline, int64_t loopTimeoutDuration, + int64_t deadline, int64_t loopTimeoutDuration, const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix, const hal::utils::RequestRelocation& relocation) const { // Ensure that at most one execution is in flight at any given time. const bool alreadyInFlight = mExecutionInFlight.test_and_set(); @@ -224,17 +231,29 @@ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst:: } ExecutionResult executionResult; - const auto ret = kBurst->executeSynchronously(request, memoryIdentifierTokens, measure, - deadline, loopTimeoutDuration, &executionResult); - HANDLE_ASTATUS(ret) << "execute failed"; + if (kFeatureLevel.level >= nn::Version::Level::FEATURE_LEVEL_8) { + auto aidlHints = NN_TRY(convert(hints)); + auto aidlExtensionPrefix = NN_TRY(convert(extensionNameToPrefix)); + const auto ret = kBurst->executeSynchronouslyWithConfig( + request, memoryIdentifierTokens, + {measure, loopTimeoutDuration, std::move(aidlHints), + std::move(aidlExtensionPrefix)}, + deadline, &executionResult); + HANDLE_ASTATUS(ret) << "execute failed"; + } else { + const auto ret = + kBurst->executeSynchronously(request, memoryIdentifierTokens, measure, deadline, + loopTimeoutDuration, &executionResult); + HANDLE_ASTATUS(ret) << "execute failed"; + } if (!executionResult.outputSufficientSize) { auto canonicalOutputShapes = nn::convert(executionResult.outputShapes).value_or(std::vector<nn::OutputShape>{}); return NN_ERROR(nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, std::move(canonicalOutputShapes)) << "execution failed with " << nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE; } - auto [outputShapes, timing] = NN_TRY(hal::utils::makeExecutionFailure( - convertExecutionResults(executionResult.outputShapes, executionResult.timing))); + auto [outputShapes, timing] = + NN_TRY(convertExecutionResults(executionResult.outputShapes, executionResult.timing)); if (relocation.output) { relocation.output->flush(); @@ -244,7 +263,9 @@ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst:: nn::GeneralResult<nn::SharedExecution> Burst::createReusableExecution( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const { + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { // Ensure that request is ready for IPC. std::optional<nn::Request> maybeRequestInShared; hal::utils::RequestRelocation relocation; @@ -275,12 +296,15 @@ nn::GeneralResult<nn::SharedExecution> Burst::createReusableExecution( return BurstExecution::create(shared_from_this(), std::move(aidlRequest), std::move(memoryIdentifierTokens), aidlMeasure, - aidlLoopTimeoutDuration, std::move(relocation), std::move(holds)); + aidlLoopTimeoutDuration, hints, extensionNameToPrefix, + std::move(relocation), std::move(holds)); } nn::GeneralResult<std::shared_ptr<const BurstExecution>> BurstExecution::create( std::shared_ptr<const Burst> burst, Request request, std::vector<int64_t> memoryIdentifierTokens, bool measure, int64_t loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix, hal::utils::RequestRelocation relocation, std::vector<Burst::OptionalCacheHold> cacheHolds) { if (burst == nullptr) { @@ -289,13 +313,15 @@ nn::GeneralResult<std::shared_ptr<const BurstExecution>> BurstExecution::create( return std::make_shared<const BurstExecution>( PrivateConstructorTag{}, std::move(burst), std::move(request), - std::move(memoryIdentifierTokens), measure, loopTimeoutDuration, std::move(relocation), - std::move(cacheHolds)); + std::move(memoryIdentifierTokens), measure, loopTimeoutDuration, hints, + extensionNameToPrefix, std::move(relocation), std::move(cacheHolds)); } BurstExecution::BurstExecution(PrivateConstructorTag /*tag*/, std::shared_ptr<const Burst> burst, Request request, std::vector<int64_t> memoryIdentifierTokens, bool measure, int64_t loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix, hal::utils::RequestRelocation relocation, std::vector<Burst::OptionalCacheHold> cacheHolds) : kBurst(std::move(burst)), @@ -303,14 +329,17 @@ BurstExecution::BurstExecution(PrivateConstructorTag /*tag*/, std::shared_ptr<co kMemoryIdentifierTokens(std::move(memoryIdentifierTokens)), kMeasure(measure), kLoopTimeoutDuration(loopTimeoutDuration), + kHints(hints), + kExtensionNameToPrefix(extensionNameToPrefix), kRelocation(std::move(relocation)), kCacheHolds(std::move(cacheHolds)) {} nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> BurstExecution::compute( const nn::OptionalTimePoint& deadline) const { - const auto aidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline))); + const auto aidlDeadline = NN_TRY(convert(deadline)); return kBurst->executeInternal(kRequest, kMemoryIdentifierTokens, kMeasure, aidlDeadline, - kLoopTimeoutDuration, kRelocation); + kLoopTimeoutDuration, kHints, kExtensionNameToPrefix, + kRelocation); } nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> diff --git a/neuralnetworks/aidl/utils/src/Callbacks.cpp b/neuralnetworks/aidl/utils/src/Callbacks.cpp index 8055665228..554f3faa73 100644 --- a/neuralnetworks/aidl/utils/src/Callbacks.cpp +++ b/neuralnetworks/aidl/utils/src/Callbacks.cpp @@ -35,18 +35,20 @@ namespace { // Converts the results of IDevice::prepareModel* to the NN canonical format. On success, this // function returns with a non-null nn::SharedPreparedModel with a feature level of -// nn::Version::ANDROID_S. On failure, this function returns with the appropriate nn::GeneralError. +// nn::kVersionFeatureLevel5. On failure, this function returns with the appropriate +// nn::GeneralError. nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback( - ErrorStatus status, const std::shared_ptr<IPreparedModel>& preparedModel) { - HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status); - return NN_TRY(PreparedModel::create(preparedModel)); + ErrorStatus status, const std::shared_ptr<IPreparedModel>& preparedModel, + nn::Version featureLevel) { + HANDLE_STATUS_AIDL(status) << "model preparation failed with " << toString(status); + return NN_TRY(PreparedModel::create(preparedModel, featureLevel)); } } // namespace ndk::ScopedAStatus PreparedModelCallback::notify( ErrorStatus status, const std::shared_ptr<IPreparedModel>& preparedModel) { - mData.put(prepareModelCallback(status, preparedModel)); + mData.put(prepareModelCallback(status, preparedModel, kFeatureLevel)); return ndk::ScopedAStatus::ok(); } diff --git a/neuralnetworks/aidl/utils/src/Conversions.cpp b/neuralnetworks/aidl/utils/src/Conversions.cpp index 4b263ee49d..47c72b47af 100644 --- a/neuralnetworks/aidl/utils/src/Conversions.cpp +++ b/neuralnetworks/aidl/utils/src/Conversions.cpp @@ -25,7 +25,6 @@ #include <android-base/mapped_file.h> #include <android-base/unique_fd.h> #include <android/binder_auto_utils.h> -#include <android/hardware_buffer.h> #include <cutils/native_handle.h> #include <nnapi/OperandTypes.h> #include <nnapi/OperationTypes.h> @@ -35,8 +34,6 @@ #include <nnapi/Types.h> #include <nnapi/Validation.h> #include <nnapi/hal/CommonUtils.h> -#include <nnapi/hal/HandleError.h> -#include <vndk/hardware_buffer.h> #include <algorithm> #include <chrono> @@ -48,6 +45,11 @@ #include "Utils.h" +#ifdef __ANDROID__ +#include <android/hardware_buffer.h> +#include <vndk/hardware_buffer.h> +#endif // __ANDROID__ + #define VERIFY_NON_NEGATIVE(value) \ while (UNLIKELY(value < 0)) return NN_ERROR() @@ -55,10 +57,6 @@ while (UNLIKELY(value > std::numeric_limits<int32_t>::max())) return NN_ERROR() namespace { -template <typename Type> -constexpr std::underlying_type_t<Type> underlyingType(Type value) { - return static_cast<std::underlying_type_t<Type>>(value); -} constexpr int64_t kNoTiming = -1; @@ -68,6 +66,7 @@ namespace android::nn { namespace { using ::aidl::android::hardware::common::NativeHandle; +using ::aidl::android::hardware::neuralnetworks::utils::underlyingType; template <typename Input> using UnvalidatedConvertOutput = @@ -108,17 +107,6 @@ GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> validatedConvert( return canonical; } -GeneralResult<Handle> unvalidatedConvertHelper(const NativeHandle& aidlNativeHandle) { - std::vector<base::unique_fd> fds; - fds.reserve(aidlNativeHandle.fds.size()); - for (const auto& fd : aidlNativeHandle.fds) { - auto duplicatedFd = NN_TRY(dupFd(fd.get())); - fds.emplace_back(duplicatedFd.release()); - } - - return Handle{.fds = std::move(fds), .ints = aidlNativeHandle.ints}; -} - struct NativeHandleDeleter { void operator()(native_handle_t* handle) const { if (handle) { @@ -130,6 +118,7 @@ struct NativeHandleDeleter { using UniqueNativeHandle = std::unique_ptr<native_handle_t, NativeHandleDeleter>; +#ifdef __ANDROID__ GeneralResult<UniqueNativeHandle> nativeHandleFromAidlHandle(const NativeHandle& handle) { auto nativeHandle = UniqueNativeHandle(dupFromAidl(handle)); if (nativeHandle.get() == nullptr) { @@ -142,6 +131,7 @@ GeneralResult<UniqueNativeHandle> nativeHandleFromAidlHandle(const NativeHandle& } return nativeHandle; } +#endif // __ANDROID__ } // anonymous namespace @@ -184,26 +174,31 @@ GeneralResult<Capabilities> unvalidatedConvert(const aidl_hal::Capabilities& cap } auto operandPerformance = NN_TRY(unvalidatedConvert(capabilities.operandPerformance)); - auto table = NN_TRY(hal::utils::makeGeneralFailure( - Capabilities::OperandPerformanceTable::create(std::move(operandPerformance)), - nn::ErrorStatus::GENERAL_FAILURE)); - + auto table = + NN_TRY(Capabilities::OperandPerformanceTable::create(std::move(operandPerformance))); + + const auto relaxedFloat32toFloat16PerformanceScalar = + NN_TRY(unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceScalar)); + const auto relaxedFloat32toFloat16PerformanceTensor = + NN_TRY(unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceTensor)); + const auto ifPerformance = NN_TRY(unvalidatedConvert(capabilities.ifPerformance)); + const auto whilePerformance = NN_TRY(unvalidatedConvert(capabilities.whilePerformance)); return Capabilities{ - .relaxedFloat32toFloat16PerformanceScalar = NN_TRY( - unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceScalar)), - .relaxedFloat32toFloat16PerformanceTensor = NN_TRY( - unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceTensor)), + .relaxedFloat32toFloat16PerformanceScalar = relaxedFloat32toFloat16PerformanceScalar, + .relaxedFloat32toFloat16PerformanceTensor = relaxedFloat32toFloat16PerformanceTensor, .operandPerformance = std::move(table), - .ifPerformance = NN_TRY(unvalidatedConvert(capabilities.ifPerformance)), - .whilePerformance = NN_TRY(unvalidatedConvert(capabilities.whilePerformance)), + .ifPerformance = ifPerformance, + .whilePerformance = whilePerformance, }; } GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert( const aidl_hal::OperandPerformance& operandPerformance) { + const auto type = NN_TRY(unvalidatedConvert(operandPerformance.type)); + const auto info = NN_TRY(unvalidatedConvert(operandPerformance.info)); return Capabilities::OperandPerformance{ - .type = NN_TRY(unvalidatedConvert(operandPerformance.type)), - .info = NN_TRY(unvalidatedConvert(operandPerformance.info)), + .type = type, + .info = info, }; } @@ -239,10 +234,13 @@ GeneralResult<DataLocation> unvalidatedConvert(const aidl_hal::DataLocation& loc } GeneralResult<Operation> unvalidatedConvert(const aidl_hal::Operation& operation) { + const auto type = NN_TRY(unvalidatedConvert(operation.type)); + auto inputs = NN_TRY(toUnsigned(operation.inputs)); + auto outputs = NN_TRY(toUnsigned(operation.outputs)); return Operation{ - .type = NN_TRY(unvalidatedConvert(operation.type)), - .inputs = NN_TRY(toUnsigned(operation.inputs)), - .outputs = NN_TRY(toUnsigned(operation.outputs)), + .type = type, + .inputs = std::move(inputs), + .outputs = std::move(outputs), }; } @@ -252,14 +250,19 @@ GeneralResult<Operand::LifeTime> unvalidatedConvert( } GeneralResult<Operand> unvalidatedConvert(const aidl_hal::Operand& operand) { + const auto type = NN_TRY(unvalidatedConvert(operand.type)); + auto dimensions = NN_TRY(toUnsigned(operand.dimensions)); + const auto lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)); + const auto location = NN_TRY(unvalidatedConvert(operand.location)); + auto extraParams = NN_TRY(unvalidatedConvert(operand.extraParams)); return Operand{ - .type = NN_TRY(unvalidatedConvert(operand.type)), - .dimensions = NN_TRY(toUnsigned(operand.dimensions)), + .type = type, + .dimensions = std::move(dimensions), .scale = operand.scale, .zeroPoint = operand.zeroPoint, - .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)), - .location = NN_TRY(unvalidatedConvert(operand.location)), - .extraParams = NN_TRY(unvalidatedConvert(operand.extraParams)), + .lifetime = lifetime, + .location = location, + .extraParams = std::move(extraParams), }; } @@ -291,37 +294,47 @@ GeneralResult<Operand::SymmPerChannelQuantParams> unvalidatedConvert( } GeneralResult<Model> unvalidatedConvert(const aidl_hal::Model& model) { + auto main = NN_TRY(unvalidatedConvert(model.main)); + auto referenced = NN_TRY(unvalidatedConvert(model.referenced)); + auto operandValues = NN_TRY(unvalidatedConvert(model.operandValues)); + auto pools = NN_TRY(unvalidatedConvert(model.pools)); + auto extensionNameToPrefix = NN_TRY(unvalidatedConvert(model.extensionNameToPrefix)); return Model{ - .main = NN_TRY(unvalidatedConvert(model.main)), - .referenced = NN_TRY(unvalidatedConvert(model.referenced)), - .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)), - .pools = NN_TRY(unvalidatedConvert(model.pools)), + .main = std::move(main), + .referenced = std::move(referenced), + .operandValues = std::move(operandValues), + .pools = std::move(pools), .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16, - .extensionNameToPrefix = NN_TRY(unvalidatedConvert(model.extensionNameToPrefix)), + .extensionNameToPrefix = std::move(extensionNameToPrefix), }; } GeneralResult<Model::Subgraph> unvalidatedConvert(const aidl_hal::Subgraph& subgraph) { + auto operands = NN_TRY(unvalidatedConvert(subgraph.operands)); + auto operations = NN_TRY(unvalidatedConvert(subgraph.operations)); + auto inputIndexes = NN_TRY(toUnsigned(subgraph.inputIndexes)); + auto outputIndexes = NN_TRY(toUnsigned(subgraph.outputIndexes)); return Model::Subgraph{ - .operands = NN_TRY(unvalidatedConvert(subgraph.operands)), - .operations = NN_TRY(unvalidatedConvert(subgraph.operations)), - .inputIndexes = NN_TRY(toUnsigned(subgraph.inputIndexes)), - .outputIndexes = NN_TRY(toUnsigned(subgraph.outputIndexes)), + .operands = std::move(operands), + .operations = std::move(operations), + .inputIndexes = std::move(inputIndexes), + .outputIndexes = std::move(outputIndexes), }; } -GeneralResult<Model::ExtensionNameAndPrefix> unvalidatedConvert( +GeneralResult<ExtensionNameAndPrefix> unvalidatedConvert( const aidl_hal::ExtensionNameAndPrefix& extensionNameAndPrefix) { - return Model::ExtensionNameAndPrefix{ + return ExtensionNameAndPrefix{ .name = extensionNameAndPrefix.name, .prefix = extensionNameAndPrefix.prefix, }; } GeneralResult<Extension> unvalidatedConvert(const aidl_hal::Extension& extension) { + auto operandTypes = NN_TRY(unvalidatedConvert(extension.operandTypes)); return Extension{ .name = extension.name, - .operandTypes = NN_TRY(unvalidatedConvert(extension.operandTypes)), + .operandTypes = std::move(operandTypes), }; } @@ -337,8 +350,9 @@ GeneralResult<Extension::OperandTypeInformation> unvalidatedConvert( } GeneralResult<OutputShape> unvalidatedConvert(const aidl_hal::OutputShape& outputShape) { + auto dimensions = NN_TRY(toUnsigned(outputShape.dimensions)); return OutputShape{ - .dimensions = NN_TRY(toUnsigned(outputShape.dimensions)), + .dimensions = std::move(dimensions), .isSufficient = outputShape.isSufficient, }; } @@ -357,8 +371,9 @@ GeneralResult<SharedMemory> unvalidatedConvert(const aidl_hal::Memory& memory) { return NN_ERROR() << "Memory: size must be <= std::numeric_limits<size_t>::max()"; } + auto fd = NN_TRY(dupFd(ashmem.fd.get())); auto handle = Memory::Ashmem{ - .fd = NN_TRY(dupFd(ashmem.fd.get())), + .fd = std::move(fd), .size = static_cast<size_t>(ashmem.size), }; return std::make_shared<const Memory>(Memory{.handle = std::move(handle)}); @@ -382,6 +397,7 @@ GeneralResult<SharedMemory> unvalidatedConvert(const aidl_hal::Memory& memory) { return createSharedMemoryFromFd(size, prot, fd, offset); } case Tag::hardwareBuffer: { +#ifdef __ANDROID__ const auto& hardwareBuffer = memory.get<Tag::hardwareBuffer>(); const UniqueNativeHandle handle = @@ -404,9 +420,14 @@ GeneralResult<SharedMemory> unvalidatedConvert(const aidl_hal::Memory& memory) { } return createSharedMemoryFromAHWB(ahwb, /*takeOwnership=*/true); +#else // __ANDROID__ + LOG(FATAL) << "GeneralResult<SharedMemory> unvalidatedConvert(const aidl_hal::Memory& " + "memory): Not Available on Host Build"; + return NN_ERROR() << "createFromHandle failed"; +#endif // __ANDROID__ } } - return NN_ERROR() << "Unrecognized Memory::Tag: " << memory.getTag(); + return NN_ERROR() << "Unrecognized Memory::Tag: " << underlyingType(memory.getTag()); } GeneralResult<Timing> unvalidatedConvert(const aidl_hal::Timing& timing) { @@ -431,7 +452,8 @@ GeneralResult<Model::OperandValues> unvalidatedConvert(const std::vector<uint8_t } GeneralResult<BufferDesc> unvalidatedConvert(const aidl_hal::BufferDesc& bufferDesc) { - return BufferDesc{.dimensions = NN_TRY(toUnsigned(bufferDesc.dimensions))}; + auto dimensions = NN_TRY(toUnsigned(bufferDesc.dimensions)); + return BufferDesc{.dimensions = std::move(dimensions)}; } GeneralResult<BufferRole> unvalidatedConvert(const aidl_hal::BufferRole& bufferRole) { @@ -445,20 +467,25 @@ GeneralResult<BufferRole> unvalidatedConvert(const aidl_hal::BufferRole& bufferR } GeneralResult<Request> unvalidatedConvert(const aidl_hal::Request& request) { + auto inputs = NN_TRY(unvalidatedConvert(request.inputs)); + auto outputs = NN_TRY(unvalidatedConvert(request.outputs)); + auto pools = NN_TRY(unvalidatedConvert(request.pools)); return Request{ - .inputs = NN_TRY(unvalidatedConvert(request.inputs)), - .outputs = NN_TRY(unvalidatedConvert(request.outputs)), - .pools = NN_TRY(unvalidatedConvert(request.pools)), + .inputs = std::move(inputs), + .outputs = std::move(outputs), + .pools = std::move(pools), }; } GeneralResult<Request::Argument> unvalidatedConvert(const aidl_hal::RequestArgument& argument) { const auto lifetime = argument.hasNoValue ? Request::Argument::LifeTime::NO_VALUE : Request::Argument::LifeTime::POOL; + const auto location = NN_TRY(unvalidatedConvert(argument.location)); + auto dimensions = NN_TRY(toUnsigned(argument.dimensions)); return Request::Argument{ .lifetime = lifetime, - .location = NN_TRY(unvalidatedConvert(argument.location)), - .dimensions = NN_TRY(toUnsigned(argument.dimensions)), + .location = location, + .dimensions = std::move(dimensions), }; } @@ -498,20 +525,22 @@ GeneralResult<ExecutionPreference> unvalidatedConvert( return static_cast<ExecutionPreference>(executionPreference); } -GeneralResult<SharedHandle> unvalidatedConvert(const NativeHandle& aidlNativeHandle) { - return std::make_shared<const Handle>(NN_TRY(unvalidatedConvertHelper(aidlNativeHandle))); -} - GeneralResult<std::vector<Operation>> unvalidatedConvert( const std::vector<aidl_hal::Operation>& operations) { return unvalidatedConvertVec(operations); } -GeneralResult<SyncFence> unvalidatedConvert(const ndk::ScopedFileDescriptor& syncFence) { - auto duplicatedFd = NN_TRY(dupFd(syncFence.get())); - return SyncFence::create(std::move(duplicatedFd)); +GeneralResult<SharedHandle> unvalidatedConvert(const ndk::ScopedFileDescriptor& handle) { + auto duplicatedFd = NN_TRY(dupFd(handle.get())); + return std::make_shared<const Handle>(std::move(duplicatedFd)); } +#ifdef NN_AIDL_V4_OR_ABOVE +GeneralResult<TokenValuePair> unvalidatedConvert(const aidl_hal::TokenValuePair& tokenValuePair) { + return TokenValuePair{.token = tokenValuePair.token, .value = tokenValuePair.value}; +} +#endif // NN_AIDL_V4_OR_ABOVE + GeneralResult<Capabilities> convert(const aidl_hal::Capabilities& capabilities) { return validatedConvert(capabilities); } @@ -553,8 +582,12 @@ GeneralResult<Timing> convert(const aidl_hal::Timing& timing) { return validatedConvert(timing); } -GeneralResult<SyncFence> convert(const ndk::ScopedFileDescriptor& syncFence) { - return validatedConvert(syncFence); +GeneralResult<SharedHandle> convert(const ndk::ScopedFileDescriptor& handle) { + return validatedConvert(handle); +} + +GeneralResult<BufferDesc> convert(const aidl_hal::BufferDesc& bufferDesc) { + return validatedConvert(bufferDesc); } GeneralResult<std::vector<Extension>> convert(const std::vector<aidl_hal::Extension>& extension) { @@ -564,12 +597,32 @@ GeneralResult<std::vector<Extension>> convert(const std::vector<aidl_hal::Extens GeneralResult<std::vector<SharedMemory>> convert(const std::vector<aidl_hal::Memory>& memories) { return validatedConvert(memories); } +GeneralResult<std::vector<ExtensionNameAndPrefix>> convert( + const std::vector<aidl_hal::ExtensionNameAndPrefix>& extensionNameAndPrefix) { + return unvalidatedConvert(extensionNameAndPrefix); +} + +#ifdef NN_AIDL_V4_OR_ABOVE +GeneralResult<std::vector<TokenValuePair>> convert( + const std::vector<aidl_hal::TokenValuePair>& metaData) { + return validatedConvert(metaData); +} +#endif // NN_AIDL_V4_OR_ABOVE GeneralResult<std::vector<OutputShape>> convert( const std::vector<aidl_hal::OutputShape>& outputShapes) { return validatedConvert(outputShapes); } +GeneralResult<std::vector<SharedHandle>> convert( + const std::vector<ndk::ScopedFileDescriptor>& handles) { + return validatedConvert(handles); +} + +GeneralResult<std::vector<BufferRole>> convert(const std::vector<aidl_hal::BufferRole>& roles) { + return validatedConvert(roles); +} + GeneralResult<std::vector<uint32_t>> toUnsigned(const std::vector<int32_t>& vec) { if (!std::all_of(vec.begin(), vec.end(), [](int32_t v) { return v >= 0; })) { return NN_ERROR() << "Negative value passed to conversion from signed to unsigned"; @@ -582,53 +635,7 @@ GeneralResult<std::vector<uint32_t>> toUnsigned(const std::vector<int32_t>& vec) namespace aidl::android::hardware::neuralnetworks::utils { namespace { -template <typename Input> -using UnvalidatedConvertOutput = - std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>; - -template <typename Type> -nn::GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvertVec( - const std::vector<Type>& arguments) { - std::vector<UnvalidatedConvertOutput<Type>> halObject; - halObject.reserve(arguments.size()); - for (const auto& argument : arguments) { - halObject.push_back(NN_TRY(unvalidatedConvert(argument))); - } - return halObject; -} - -template <typename Type> -nn::GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvert( - const std::vector<Type>& arguments) { - return unvalidatedConvertVec(arguments); -} - -template <typename Type> -nn::GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& canonical) { - NN_TRY(compliantVersion(canonical)); - return utils::unvalidatedConvert(canonical); -} - -template <typename Type> -nn::GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> validatedConvert( - const std::vector<Type>& arguments) { - std::vector<UnvalidatedConvertOutput<Type>> halObject(arguments.size()); - for (size_t i = 0; i < arguments.size(); ++i) { - halObject[i] = NN_TRY(validatedConvert(arguments[i])); - } - return halObject; -} - -nn::GeneralResult<common::NativeHandle> unvalidatedConvert(const nn::Handle& handle) { - common::NativeHandle aidlNativeHandle; - aidlNativeHandle.fds.reserve(handle.fds.size()); - for (const auto& fd : handle.fds) { - auto duplicatedFd = NN_TRY(nn::dupFd(fd.get())); - aidlNativeHandle.fds.emplace_back(duplicatedFd.release()); - } - aidlNativeHandle.ints = handle.ints; - return aidlNativeHandle; -} +using utils::unvalidatedConvert; // Helper template for std::visit template <class... Ts> @@ -636,8 +643,9 @@ struct overloaded : Ts... { using Ts::operator()...; }; template <class... Ts> -overloaded(Ts...)->overloaded<Ts...>; +overloaded(Ts...) -> overloaded<Ts...>; +#ifdef __ANDROID__ nn::GeneralResult<common::NativeHandle> aidlHandleFromNativeHandle( const native_handle_t& nativeHandle) { auto handle = ::android::dupToAidl(&nativeHandle); @@ -647,6 +655,7 @@ nn::GeneralResult<common::NativeHandle> aidlHandleFromNativeHandle( } return handle; } +#endif // __ANDROID__ nn::GeneralResult<Memory> unvalidatedConvert(const nn::Memory::Ashmem& memory) { if constexpr (std::numeric_limits<size_t>::max() > std::numeric_limits<int64_t>::max()) { @@ -694,6 +703,7 @@ nn::GeneralResult<Memory> unvalidatedConvert(const nn::Memory::Fd& memory) { } nn::GeneralResult<Memory> unvalidatedConvert(const nn::Memory::HardwareBuffer& memory) { +#ifdef __ANDROID__ const native_handle_t* nativeHandle = AHardwareBuffer_getNativeHandle(memory.handle.get()); if (nativeHandle == nullptr) { return (NN_ERROR() << "unvalidatedConvert failed because AHardwareBuffer_getNativeHandle " @@ -721,6 +731,12 @@ nn::GeneralResult<Memory> unvalidatedConvert(const nn::Memory::HardwareBuffer& m .handle = std::move(handle), }; return Memory::make<Memory::Tag::hardwareBuffer>(std::move(hardwareBuffer)); +#else // __ANDROID__ + LOG(FATAL) << "nn::GeneralResult<Memory> unvalidatedConvert(const nn::Memory::HardwareBuffer& " + "memory): Not Available on Host Build"; + (void)memory; + return (NN_ERROR() << "unvalidatedConvert failed").operator nn::GeneralResult<Memory>(); +#endif // __ANDROID__ } nn::GeneralResult<Memory> unvalidatedConvert(const nn::Memory::Unknown& /*memory*/) { @@ -729,6 +745,75 @@ nn::GeneralResult<Memory> unvalidatedConvert(const nn::Memory::Unknown& /*memory operator nn::GeneralResult<Memory>(); } +nn::GeneralResult<PerformanceInfo> unvalidatedConvert( + const nn::Capabilities::PerformanceInfo& info) { + return PerformanceInfo{.execTime = info.execTime, .powerUsage = info.powerUsage}; +} + +nn::GeneralResult<OperandPerformance> unvalidatedConvert( + const nn::Capabilities::OperandPerformance& operandPerformance) { + const auto type = NN_TRY(unvalidatedConvert(operandPerformance.type)); + const auto info = NN_TRY(unvalidatedConvert(operandPerformance.info)); + return OperandPerformance{.type = type, .info = info}; +} + +nn::GeneralResult<std::vector<OperandPerformance>> unvalidatedConvert( + const nn::Capabilities::OperandPerformanceTable& table) { + std::vector<OperandPerformance> operandPerformances; + operandPerformances.reserve(table.asVector().size()); + for (const auto& operandPerformance : table.asVector()) { + operandPerformances.push_back(NN_TRY(unvalidatedConvert(operandPerformance))); + } + return operandPerformances; +} + +nn::GeneralResult<ExtensionOperandTypeInformation> unvalidatedConvert( + const nn::Extension::OperandTypeInformation& info) { + return ExtensionOperandTypeInformation{.type = info.type, + .isTensor = info.isTensor, + .byteSize = static_cast<int32_t>(info.byteSize)}; +} + +nn::GeneralResult<int64_t> unvalidatedConvert(const nn::Duration& duration) { + if (duration < nn::Duration::zero()) { + return NN_ERROR() << "Unable to convert invalid (negative) duration"; + } + constexpr std::chrono::nanoseconds::rep kIntMax = std::numeric_limits<int64_t>::max(); + const auto count = duration.count(); + return static_cast<int64_t>(std::min(count, kIntMax)); +} + +template <typename Input> +using UnvalidatedConvertOutput = + std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>; + +template <typename Type> +nn::GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvert( + const std::vector<Type>& arguments) { + std::vector<UnvalidatedConvertOutput<Type>> halObject; + halObject.reserve(arguments.size()); + for (const auto& argument : arguments) { + halObject.push_back(NN_TRY(unvalidatedConvert(argument))); + } + return halObject; +} + +template <typename Type> +nn::GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& canonical) { + NN_TRY(compliantVersion(canonical)); + return utils::unvalidatedConvert(canonical); +} + +template <typename Type> +nn::GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> validatedConvert( + const std::vector<Type>& arguments) { + std::vector<UnvalidatedConvertOutput<Type>> halObject(arguments.size()); + for (size_t i = 0; i < arguments.size(); ++i) { + halObject[i] = NN_TRY(validatedConvert(arguments[i])); + } + return halObject; +} + } // namespace nn::GeneralResult<std::vector<uint8_t>> unvalidatedConvert(const nn::CacheToken& cacheToken) { @@ -736,7 +821,8 @@ nn::GeneralResult<std::vector<uint8_t>> unvalidatedConvert(const nn::CacheToken& } nn::GeneralResult<BufferDesc> unvalidatedConvert(const nn::BufferDesc& bufferDesc) { - return BufferDesc{.dimensions = NN_TRY(toSigned(bufferDesc.dimensions))}; + auto dimensions = NN_TRY(toSigned(bufferDesc.dimensions)); + return BufferDesc{.dimensions = std::move(dimensions)}; } nn::GeneralResult<BufferRole> unvalidatedConvert(const nn::BufferRole& bufferRole) { @@ -751,13 +837,21 @@ nn::GeneralResult<BufferRole> unvalidatedConvert(const nn::BufferRole& bufferRol }; } -nn::GeneralResult<bool> unvalidatedConvert(const nn::MeasureTiming& measureTiming) { - return measureTiming == nn::MeasureTiming::YES; +nn::GeneralResult<DeviceType> unvalidatedConvert(const nn::DeviceType& deviceType) { + switch (deviceType) { + case nn::DeviceType::UNKNOWN: + break; + case nn::DeviceType::OTHER: + case nn::DeviceType::CPU: + case nn::DeviceType::GPU: + case nn::DeviceType::ACCELERATOR: + return static_cast<DeviceType>(deviceType); + } + return NN_ERROR() << "Invalid DeviceType " << deviceType; } -nn::GeneralResult<common::NativeHandle> unvalidatedConvert(const nn::SharedHandle& sharedHandle) { - CHECK(sharedHandle != nullptr); - return unvalidatedConvert(*sharedHandle); +nn::GeneralResult<bool> unvalidatedConvert(const nn::MeasureTiming& measureTiming) { + return measureTiming == nn::MeasureTiming::YES; } nn::GeneralResult<Memory> unvalidatedConvert(const nn::SharedMemory& memory) { @@ -787,7 +881,8 @@ nn::GeneralResult<ErrorStatus> unvalidatedConvert(const nn::ErrorStatus& errorSt } nn::GeneralResult<OutputShape> unvalidatedConvert(const nn::OutputShape& outputShape) { - return OutputShape{.dimensions = NN_TRY(toSigned(outputShape.dimensions)), + auto dimensions = NN_TRY(toSigned(outputShape.dimensions)); + return OutputShape{.dimensions = std::move(dimensions), .isSufficient = outputShape.isSufficient}; } @@ -855,14 +950,19 @@ nn::GeneralResult<std::optional<OperandExtraParams>> unvalidatedConvert( } nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand) { + const auto type = NN_TRY(unvalidatedConvert(operand.type)); + auto dimensions = NN_TRY(toSigned(operand.dimensions)); + const auto lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)); + const auto location = NN_TRY(unvalidatedConvert(operand.location)); + auto extraParams = NN_TRY(unvalidatedConvert(operand.extraParams)); return Operand{ - .type = NN_TRY(unvalidatedConvert(operand.type)), - .dimensions = NN_TRY(toSigned(operand.dimensions)), + .type = type, + .dimensions = std::move(dimensions), .scale = operand.scale, .zeroPoint = operand.zeroPoint, - .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)), - .location = NN_TRY(unvalidatedConvert(operand.location)), - .extraParams = NN_TRY(unvalidatedConvert(operand.extraParams)), + .lifetime = lifetime, + .location = location, + .extraParams = std::move(extraParams), }; } @@ -874,19 +974,26 @@ nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& ope } nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation) { + const auto type = NN_TRY(unvalidatedConvert(operation.type)); + auto inputs = NN_TRY(toSigned(operation.inputs)); + auto outputs = NN_TRY(toSigned(operation.outputs)); return Operation{ - .type = NN_TRY(unvalidatedConvert(operation.type)), - .inputs = NN_TRY(toSigned(operation.inputs)), - .outputs = NN_TRY(toSigned(operation.outputs)), + .type = type, + .inputs = std::move(inputs), + .outputs = std::move(outputs), }; } nn::GeneralResult<Subgraph> unvalidatedConvert(const nn::Model::Subgraph& subgraph) { + auto operands = NN_TRY(unvalidatedConvert(subgraph.operands)); + auto operations = NN_TRY(unvalidatedConvert(subgraph.operations)); + auto inputIndexes = NN_TRY(toSigned(subgraph.inputIndexes)); + auto outputIndexes = NN_TRY(toSigned(subgraph.outputIndexes)); return Subgraph{ - .operands = NN_TRY(unvalidatedConvert(subgraph.operands)), - .operations = NN_TRY(unvalidatedConvert(subgraph.operations)), - .inputIndexes = NN_TRY(toSigned(subgraph.inputIndexes)), - .outputIndexes = NN_TRY(toSigned(subgraph.outputIndexes)), + .operands = std::move(operands), + .operations = std::move(operations), + .inputIndexes = std::move(inputIndexes), + .outputIndexes = std::move(outputIndexes), }; } @@ -896,7 +1003,7 @@ nn::GeneralResult<std::vector<uint8_t>> unvalidatedConvert( } nn::GeneralResult<ExtensionNameAndPrefix> unvalidatedConvert( - const nn::Model::ExtensionNameAndPrefix& extensionNameToPrefix) { + const nn::ExtensionNameAndPrefix& extensionNameToPrefix) { return ExtensionNameAndPrefix{ .name = extensionNameToPrefix.name, .prefix = extensionNameToPrefix.prefix, @@ -904,13 +1011,23 @@ nn::GeneralResult<ExtensionNameAndPrefix> unvalidatedConvert( } nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) { + if (!hal::utils::hasNoPointerData(model)) { + return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) + << "Model cannot be unvalidatedConverted because it contains pointer-based memory"; + } + + auto main = NN_TRY(unvalidatedConvert(model.main)); + auto referenced = NN_TRY(unvalidatedConvert(model.referenced)); + auto operandValues = NN_TRY(unvalidatedConvert(model.operandValues)); + auto pools = NN_TRY(unvalidatedConvert(model.pools)); + auto extensionNameToPrefix = NN_TRY(unvalidatedConvert(model.extensionNameToPrefix)); return Model{ - .main = NN_TRY(unvalidatedConvert(model.main)), - .referenced = NN_TRY(unvalidatedConvert(model.referenced)), - .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)), - .pools = NN_TRY(unvalidatedConvert(model.pools)), + .main = std::move(main), + .referenced = std::move(referenced), + .operandValues = std::move(operandValues), + .pools = std::move(pools), .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16, - .extensionNameToPrefix = NN_TRY(unvalidatedConvert(model.extensionNameToPrefix)), + .extensionNameToPrefix = std::move(extensionNameToPrefix), }; } @@ -919,10 +1036,18 @@ nn::GeneralResult<Priority> unvalidatedConvert(const nn::Priority& priority) { } nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request) { + if (!hal::utils::hasNoPointerData(request)) { + return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) + << "Request cannot be unvalidatedConverted because it contains pointer-based memory"; + } + + auto inputs = NN_TRY(unvalidatedConvert(request.inputs)); + auto outputs = NN_TRY(unvalidatedConvert(request.outputs)); + auto pools = NN_TRY(unvalidatedConvert(request.pools)); return Request{ - .inputs = NN_TRY(unvalidatedConvert(request.inputs)), - .outputs = NN_TRY(unvalidatedConvert(request.outputs)), - .pools = NN_TRY(unvalidatedConvert(request.pools)), + .inputs = std::move(inputs), + .outputs = std::move(outputs), + .pools = std::move(pools), }; } @@ -933,10 +1058,12 @@ nn::GeneralResult<RequestArgument> unvalidatedConvert( << "Request cannot be unvalidatedConverted because it contains pointer-based memory"; } const bool hasNoValue = requestArgument.lifetime == nn::Request::Argument::LifeTime::NO_VALUE; + const auto location = NN_TRY(unvalidatedConvert(requestArgument.location)); + auto dimensions = NN_TRY(toSigned(requestArgument.dimensions)); return RequestArgument{ .hasNoValue = hasNoValue, - .location = NN_TRY(unvalidatedConvert(requestArgument.location)), - .dimensions = NN_TRY(toSigned(requestArgument.dimensions)), + .location = location, + .dimensions = std::move(dimensions), }; } @@ -963,21 +1090,14 @@ nn::GeneralResult<RequestMemoryPool> unvalidatedConvert(const nn::Request::Memor } nn::GeneralResult<Timing> unvalidatedConvert(const nn::Timing& timing) { + const auto timeOnDeviceNs = NN_TRY(unvalidatedConvert(timing.timeOnDevice)); + const auto timeInDriverNs = NN_TRY(unvalidatedConvert(timing.timeInDriver)); return Timing{ - .timeOnDeviceNs = NN_TRY(unvalidatedConvert(timing.timeOnDevice)), - .timeInDriverNs = NN_TRY(unvalidatedConvert(timing.timeInDriver)), + .timeOnDeviceNs = timeOnDeviceNs, + .timeInDriverNs = timeInDriverNs, }; } -nn::GeneralResult<int64_t> unvalidatedConvert(const nn::Duration& duration) { - if (duration < nn::Duration::zero()) { - return NN_ERROR() << "Unable to convert invalid (negative) duration"; - } - constexpr std::chrono::nanoseconds::rep kIntMax = std::numeric_limits<int64_t>::max(); - const auto count = duration.count(); - return static_cast<int64_t>(std::min(count, kIntMax)); -} - nn::GeneralResult<int64_t> unvalidatedConvert(const nn::OptionalDuration& optionalDuration) { if (!optionalDuration.has_value()) { return kNoTiming; @@ -997,19 +1117,38 @@ nn::GeneralResult<ndk::ScopedFileDescriptor> unvalidatedConvert(const nn::SyncFe return ndk::ScopedFileDescriptor(duplicatedFd.release()); } -nn::GeneralResult<ndk::ScopedFileDescriptor> unvalidatedConvertCache( - const nn::SharedHandle& handle) { - if (handle->ints.size() != 0) { - NN_ERROR() << "Cache handle must not contain ints"; - } - if (handle->fds.size() != 1) { - NN_ERROR() << "Cache handle must contain exactly one fd but contains " - << handle->fds.size(); - } - auto duplicatedFd = NN_TRY(nn::dupFd(handle->fds.front().get())); +nn::GeneralResult<ndk::ScopedFileDescriptor> unvalidatedConvert(const nn::SharedHandle& handle) { + auto duplicatedFd = NN_TRY(nn::dupFd(handle->get())); return ndk::ScopedFileDescriptor(duplicatedFd.release()); } +nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities) { + const auto relaxedFloat32toFloat16PerformanceTensor = + NN_TRY(unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceTensor)); + const auto relaxedFloat32toFloat16PerformanceScalar = + NN_TRY(unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceScalar)); + auto operandPerformance = NN_TRY(unvalidatedConvert(capabilities.operandPerformance)); + const auto ifPerformance = NN_TRY(unvalidatedConvert(capabilities.ifPerformance)); + const auto whilePerformance = NN_TRY(unvalidatedConvert(capabilities.whilePerformance)); + return Capabilities{ + .relaxedFloat32toFloat16PerformanceTensor = relaxedFloat32toFloat16PerformanceTensor, + .relaxedFloat32toFloat16PerformanceScalar = relaxedFloat32toFloat16PerformanceScalar, + .operandPerformance = std::move(operandPerformance), + .ifPerformance = ifPerformance, + .whilePerformance = whilePerformance, + }; +} + +nn::GeneralResult<Extension> unvalidatedConvert(const nn::Extension& extension) { + auto operandTypes = NN_TRY(unvalidatedConvert(extension.operandTypes)); + return Extension{.name = extension.name, .operandTypes = std::move(operandTypes)}; +} +#ifdef NN_AIDL_V4_OR_ABOVE +nn::GeneralResult<TokenValuePair> unvalidatedConvert(const nn::TokenValuePair& tokenValuePair) { + return TokenValuePair{.token = tokenValuePair.token, .value = tokenValuePair.value}; +} +#endif // NN_AIDL_V4_OR_ABOVE + nn::GeneralResult<std::vector<uint8_t>> convert(const nn::CacheToken& cacheToken) { return validatedConvert(cacheToken); } @@ -1018,6 +1157,10 @@ nn::GeneralResult<BufferDesc> convert(const nn::BufferDesc& bufferDesc) { return validatedConvert(bufferDesc); } +nn::GeneralResult<DeviceType> convert(const nn::DeviceType& deviceType) { + return validatedConvert(deviceType); +} + nn::GeneralResult<bool> convert(const nn::MeasureTiming& measureTiming) { return validatedConvert(measureTiming); } @@ -1058,6 +1201,14 @@ nn::GeneralResult<int64_t> convert(const nn::OptionalTimePoint& outputShapes) { return validatedConvert(outputShapes); } +nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities) { + return validatedConvert(capabilities); +} + +nn::GeneralResult<Extension> convert(const nn::Extension& extension) { + return validatedConvert(extension); +} + nn::GeneralResult<std::vector<BufferRole>> convert(const std::vector<nn::BufferRole>& bufferRoles) { return validatedConvert(bufferRoles); } @@ -1069,22 +1220,28 @@ nn::GeneralResult<std::vector<OutputShape>> convert( nn::GeneralResult<std::vector<ndk::ScopedFileDescriptor>> convert( const std::vector<nn::SharedHandle>& cacheHandles) { - const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(cacheHandles))); - if (version > kVersion) { - return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion; - } - std::vector<ndk::ScopedFileDescriptor> cacheFds; - cacheFds.reserve(cacheHandles.size()); - for (const auto& cacheHandle : cacheHandles) { - cacheFds.push_back(NN_TRY(unvalidatedConvertCache(cacheHandle))); - } - return cacheFds; + return validatedConvert(cacheHandles); } nn::GeneralResult<std::vector<ndk::ScopedFileDescriptor>> convert( const std::vector<nn::SyncFence>& syncFences) { return validatedConvert(syncFences); } +nn::GeneralResult<std::vector<ExtensionNameAndPrefix>> convert( + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) { + return unvalidatedConvert(extensionNameToPrefix); +} + +#ifdef NN_AIDL_V4_OR_ABOVE +nn::GeneralResult<std::vector<TokenValuePair>> convert( + const std::vector<nn::TokenValuePair>& metaData) { + return validatedConvert(metaData); +} +#endif // NN_AIDL_V4_OR_ABOVE + +nn::GeneralResult<std::vector<Extension>> convert(const std::vector<nn::Extension>& extensions) { + return validatedConvert(extensions); +} nn::GeneralResult<std::vector<int32_t>> toSigned(const std::vector<uint32_t>& vec) { if (!std::all_of(vec.begin(), vec.end(), @@ -1094,4 +1251,8 @@ nn::GeneralResult<std::vector<int32_t>> toSigned(const std::vector<uint32_t>& ve return std::vector<int32_t>(vec.begin(), vec.end()); } +std::vector<uint8_t> toVec(const std::array<uint8_t, IDevice::BYTE_SIZE_OF_CACHE_TOKEN>& token) { + return std::vector<uint8_t>(token.begin(), token.end()); +} + } // namespace aidl::android::hardware::neuralnetworks::utils diff --git a/neuralnetworks/aidl/utils/src/Device.cpp b/neuralnetworks/aidl/utils/src/Device.cpp index e80de0be76..b64a40dcaa 100644 --- a/neuralnetworks/aidl/utils/src/Device.cpp +++ b/neuralnetworks/aidl/utils/src/Device.cpp @@ -125,7 +125,7 @@ nn::GeneralResult<std::pair<uint32_t, uint32_t>> getNumberOfCacheFilesNeededFrom } // namespace nn::GeneralResult<std::shared_ptr<const Device>> Device::create( - std::string name, std::shared_ptr<aidl_hal::IDevice> device) { + std::string name, std::shared_ptr<aidl_hal::IDevice> device, nn::Version featureLevel) { if (name.empty()) { return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "aidl_hal::utils::Device::create must have non-empty name"; @@ -143,18 +143,19 @@ nn::GeneralResult<std::shared_ptr<const Device>> Device::create( auto deathHandler = NN_TRY(DeathHandler::create(device)); return std::make_shared<const Device>( - PrivateConstructorTag{}, std::move(name), std::move(versionString), deviceType, - std::move(extensions), std::move(capabilities), numberOfCacheFilesNeeded, + PrivateConstructorTag{}, std::move(name), std::move(versionString), featureLevel, + deviceType, std::move(extensions), std::move(capabilities), numberOfCacheFilesNeeded, std::move(device), std::move(deathHandler)); } Device::Device(PrivateConstructorTag /*tag*/, std::string name, std::string versionString, - nn::DeviceType deviceType, std::vector<nn::Extension> extensions, - nn::Capabilities capabilities, + nn::Version featureLevel, nn::DeviceType deviceType, + std::vector<nn::Extension> extensions, nn::Capabilities capabilities, std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded, std::shared_ptr<aidl_hal::IDevice> device, DeathHandler deathHandler) : kName(std::move(name)), kVersionString(std::move(versionString)), + kFeatureLevel(featureLevel), kDeviceType(deviceType), kExtensions(std::move(extensions)), kCapabilities(std::move(capabilities)), @@ -171,7 +172,7 @@ const std::string& Device::getVersionString() const { } nn::Version Device::getFeatureLevel() const { - return nn::Version::ANDROID_S; + return kFeatureLevel; } nn::DeviceType Device::getType() const { @@ -214,7 +215,9 @@ nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Mo nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel( const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority, nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache, - const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const { + const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { // Ensure that model is ready for IPC. std::optional<nn::Model> maybeModelInShared; const nn::Model& modelInShared = @@ -224,17 +227,28 @@ nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel( const auto aidlPreference = NN_TRY(convert(preference)); const auto aidlPriority = NN_TRY(convert(priority)); const auto aidlDeadline = NN_TRY(convert(deadline)); - const auto aidlModelCache = NN_TRY(convert(modelCache)); - const auto aidlDataCache = NN_TRY(convert(dataCache)); - const auto aidlToken = NN_TRY(convert(token)); + auto aidlModelCache = NN_TRY(convert(modelCache)); + auto aidlDataCache = NN_TRY(convert(dataCache)); - const auto cb = ndk::SharedRefBase::make<PreparedModelCallback>(); + const auto cb = ndk::SharedRefBase::make<PreparedModelCallback>(kFeatureLevel); const auto scoped = kDeathHandler.protectCallback(cb.get()); + if (kFeatureLevel.level >= nn::Version::Level::FEATURE_LEVEL_8) { + auto aidlHints = NN_TRY(convert(hints)); + auto aidlExtensionPrefix = NN_TRY(convert(extensionNameToPrefix)); + const auto ret = kDevice->prepareModelWithConfig( + aidlModel, + {aidlPreference, aidlPriority, aidlDeadline, std::move(aidlModelCache), + std::move(aidlDataCache), token, std::move(aidlHints), + std::move(aidlExtensionPrefix)}, + cb); + HANDLE_ASTATUS(ret) << "prepareModel failed"; + return cb->get(); + } + const auto aidlToken = NN_TRY(convert(token)); const auto ret = kDevice->prepareModel(aidlModel, aidlPreference, aidlPriority, aidlDeadline, aidlModelCache, aidlDataCache, aidlToken, cb); HANDLE_ASTATUS(ret) << "prepareModel failed"; - return cb->get(); } @@ -246,7 +260,7 @@ nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModelFromCache( const auto aidlDataCache = NN_TRY(convert(dataCache)); const auto aidlToken = NN_TRY(convert(token)); - const auto cb = ndk::SharedRefBase::make<PreparedModelCallback>(); + const auto cb = ndk::SharedRefBase::make<PreparedModelCallback>(kFeatureLevel); const auto scoped = kDeathHandler.protectCallback(cb.get()); const auto ret = kDevice->prepareModelFromCache(aidlDeadline, aidlModelCache, aidlDataCache, diff --git a/neuralnetworks/aidl/utils/src/Execution.cpp b/neuralnetworks/aidl/utils/src/Execution.cpp index 2aee8a6713..2fd88aff36 100644 --- a/neuralnetworks/aidl/utils/src/Execution.cpp +++ b/neuralnetworks/aidl/utils/src/Execution.cpp @@ -25,7 +25,6 @@ #include <nnapi/Result.h> #include <nnapi/Types.h> #include <nnapi/hal/CommonUtils.h> -#include <nnapi/hal/HandleError.h> #include <memory> #include <utility> @@ -36,44 +35,61 @@ namespace aidl::android::hardware::neuralnetworks::utils { -nn::GeneralResult<std::shared_ptr<const Execution>> Execution::create( - std::shared_ptr<const PreparedModel> preparedModel, Request request, - hal::utils::RequestRelocation relocation, bool measure, int64_t loopTimeoutDuration) { +nn::GeneralResult<std::shared_ptr<const ExecutionWithCachedRequest>> +ExecutionWithCachedRequest::create(std::shared_ptr<const PreparedModel> preparedModel, + Request request, hal::utils::RequestRelocation relocation, + bool measure, int64_t loopTimeoutDuration) { if (preparedModel == nullptr) { - return NN_ERROR() << "aidl::utils::Execution::create must have non-null preparedModel"; + return NN_ERROR() << "aidl::utils::ExecutionWithCachedRequest::create must have non-null " + "preparedModel"; } - return std::make_shared<const Execution>(PrivateConstructorTag{}, std::move(preparedModel), - std::move(request), std::move(relocation), measure, - loopTimeoutDuration); + return std::make_shared<const ExecutionWithCachedRequest>( + PrivateConstructorTag{}, std::move(preparedModel), std::move(request), + std::move(relocation), measure, loopTimeoutDuration); } -Execution::Execution(PrivateConstructorTag /*tag*/, - std::shared_ptr<const PreparedModel> preparedModel, Request request, - hal::utils::RequestRelocation relocation, bool measure, - int64_t loopTimeoutDuration) +ExecutionWithCachedRequest::ExecutionWithCachedRequest( + PrivateConstructorTag /*tag*/, std::shared_ptr<const PreparedModel> preparedModel, + Request request, hal::utils::RequestRelocation relocation, bool measure, + int64_t loopTimeoutDuration) : kPreparedModel(std::move(preparedModel)), kRequest(std::move(request)), kRelocation(std::move(relocation)), kMeasure(measure), kLoopTimeoutDuration(loopTimeoutDuration) {} -nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Execution::compute( - const nn::OptionalTimePoint& deadline) const { - const auto aidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline))); +nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> +ExecutionWithCachedRequest::compute(const nn::OptionalTimePoint& deadline) const { + const auto aidlDeadline = NN_TRY(convert(deadline)); return kPreparedModel->executeInternal(kRequest, kMeasure, aidlDeadline, kLoopTimeoutDuration, - kRelocation); + {}, {}, kRelocation); } -nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> Execution::computeFenced( +nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> +ExecutionWithCachedRequest::computeFenced( const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& timeoutDurationAfterFence) const { const auto aidlWaitFor = NN_TRY(convert(waitFor)); const auto aidlDeadline = NN_TRY(convert(deadline)); const auto aidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence)); - return kPreparedModel->executeFencedInternal(kRequest, aidlWaitFor, kMeasure, aidlDeadline, - kLoopTimeoutDuration, - aidlTimeoutDurationAfterFence, kRelocation); + return kPreparedModel->executeFencedInternal( + kRequest, aidlWaitFor, kMeasure, aidlDeadline, kLoopTimeoutDuration, + aidlTimeoutDurationAfterFence, {}, {}, kRelocation); +} + +nn::GeneralResult<std::shared_ptr<const Execution>> Execution::create( + std::shared_ptr<aidl_hal::IExecution> execution, hal::utils::RequestRelocation relocation) { + if (execution == nullptr) { + return NN_ERROR() << "aidl::utils::Execution::create must have non-null execution"; + } + + return std::make_shared<const Execution>(PrivateConstructorTag{}, std::move(execution), + std::move(relocation)); } +Execution::Execution(PrivateConstructorTag /*tag*/, std::shared_ptr<aidl_hal::IExecution> execution, + hal::utils::RequestRelocation relocation) + : kExecution(std::move(execution)), kRelocation(std::move(relocation)) {} + } // namespace aidl::android::hardware::neuralnetworks::utils diff --git a/neuralnetworks/aidl/utils/src/HalUtils.cpp b/neuralnetworks/aidl/utils/src/HalUtils.cpp new file mode 100644 index 0000000000..64b6259d4f --- /dev/null +++ b/neuralnetworks/aidl/utils/src/HalUtils.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2021 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. + */ +// This file contains pre-canonical-types utility code and includes HAL +// utilities. LegacyUtils.h is the subset of these utilities that do not touch +// HAL. + +#include "HalUtils.h" + +#include "HalInterfaces.h" + +#include <android-base/logging.h> +#include <nnapi/TypeUtils.h> +#include <nnapi/hal/aidl/Conversions.h> + +#include <algorithm> +#include <iterator> +#include <type_traits> +#include <vector> + +namespace android::nn { + +std::vector<aidl_hal::OperandPerformance> nonExtensionOperandPerformance( + aidl_hal::PerformanceInfo perf) { + static constexpr ndk::enum_range<aidl_hal::OperandType> kOperandTypeRange; + std::vector<aidl_hal::OperandPerformance> ret; + ret.reserve(std::distance(kOperandTypeRange.begin(), kOperandTypeRange.end())); + for (aidl_hal::OperandType type : kOperandTypeRange) { + if (type != aidl_hal::OperandType::SUBGRAPH) { + ret.push_back(aidl_hal::OperandPerformance{type, perf}); + } + } + std::sort(ret.begin(), ret.end(), + [](const aidl_hal::OperandPerformance& a, const aidl_hal::OperandPerformance& b) { + return a.type < b.type; + }); + + return ret; +} + +void update(std::vector<aidl_hal::OperandPerformance>* operandPerformance, + aidl_hal::OperandType type, aidl_hal::PerformanceInfo perf) { + CHECK(operandPerformance != nullptr); + const auto it = std::lower_bound(operandPerformance->begin(), operandPerformance->end(), type, + [](const aidl_hal::OperandPerformance& perf, + aidl_hal::OperandType type) { return perf.type < type; }); + CHECK(it != operandPerformance->end()) + << toString(type) << " not in operand performance vector"; + it->info = perf; +} + +bool isExtensionOperandType(aidl_hal::OperandType type) { + return isExtension(convert(type).value()); +} + +bool isNonExtensionScalar(aidl_hal::OperandType type) { + return isNonExtensionScalar(convert(type).value()); +} + +} // namespace android::nn diff --git a/neuralnetworks/aidl/utils/src/InvalidDevice.cpp b/neuralnetworks/aidl/utils/src/InvalidDevice.cpp index c9d995590f..44f8ea9ff8 100644 --- a/neuralnetworks/aidl/utils/src/InvalidDevice.cpp +++ b/neuralnetworks/aidl/utils/src/InvalidDevice.cpp @@ -167,6 +167,32 @@ ndk::ScopedAStatus InvalidDevice::prepareModel( return ndk::ScopedAStatus::ok(); } +ndk::ScopedAStatus InvalidDevice::prepareModelWithConfig( + const Model& model, const PrepareModelConfig& config, + const std::shared_ptr<IPreparedModelCallback>& callback) { + if (!utils::valid(config.extensionNameToPrefix)) { + callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr); + return toAStatus(ErrorStatus::INVALID_ARGUMENT, "Invalid extensionNameToPrefix"); + } + for (const auto& hint : config.compilationHints) { + auto result = std::find_if(config.extensionNameToPrefix.begin(), + config.extensionNameToPrefix.end(), + [&hint](const ExtensionNameAndPrefix& extension) { + uint16_t prefix = static_cast<uint32_t>(hint.token) >> + IDevice::EXTENSION_TYPE_LOW_BITS_TYPE; + return prefix == extension.prefix; + }); + if (result == config.extensionNameToPrefix.end()) { + callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr); + return toAStatus(ErrorStatus::INVALID_ARGUMENT, + "Invalid token for compilation hints: " + std::to_string(hint.token)); + } + } + return prepareModel(model, config.preference, config.priority, config.deadlineNs, + config.modelCache, config.dataCache, utils::toVec(config.cacheToken), + callback); +} + ndk::ScopedAStatus InvalidDevice::prepareModelFromCache( int64_t /*deadline*/, const std::vector<ndk::ScopedFileDescriptor>& /*modelCache*/, const std::vector<ndk::ScopedFileDescriptor>& /*dataCache*/, diff --git a/neuralnetworks/aidl/utils/src/PreparedModel.cpp b/neuralnetworks/aidl/utils/src/PreparedModel.cpp index 18e7636346..7e3a31cac1 100644 --- a/neuralnetworks/aidl/utils/src/PreparedModel.cpp +++ b/neuralnetworks/aidl/utils/src/PreparedModel.cpp @@ -30,7 +30,6 @@ #include <nnapi/TypeUtils.h> #include <nnapi/Types.h> #include <nnapi/hal/CommonUtils.h> -#include <nnapi/hal/HandleError.h> #include <memory> #include <tuple> @@ -51,79 +50,138 @@ nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> convertEx nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> convertFencedExecutionResults( ErrorStatus status, const aidl_hal::Timing& timingLaunched, const aidl_hal::Timing& timingFenced) { - HANDLE_HAL_STATUS(status) << "fenced execution callback info failed with " << toString(status); + HANDLE_STATUS_AIDL(status) << "fenced execution callback info failed with " << toString(status); return std::make_pair(NN_TRY(nn::convert(timingLaunched)), NN_TRY(nn::convert(timingFenced))); } +nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> handleExecutionResult( + const ExecutionResult& result, const hal::utils::RequestRelocation& relocation) { + if (!result.outputSufficientSize) { + auto canonicalOutputShapes = + nn::convert(result.outputShapes).value_or(std::vector<nn::OutputShape>{}); + return NN_ERROR(nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, std::move(canonicalOutputShapes)) + << "execution failed with " << nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE; + } + auto [outputShapes, timing] = + NN_TRY(convertExecutionResults(result.outputShapes, result.timing)); + + if (relocation.output) { + relocation.output->flush(); + } + return std::make_pair(std::move(outputShapes), timing); +} + +nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> +handleFencedExecutionResult(const FencedExecutionResult& result, + const hal::utils::RequestRelocation& relocation) { + auto resultSyncFence = nn::SyncFence::createAsSignaled(); + if (result.syncFence.get() != -1) { + resultSyncFence = nn::SyncFence::create(NN_TRY(nn::convert(result.syncFence))).value(); + } + + auto callback = result.callback; + if (callback == nullptr) { + return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "callback is null"; + } + + // If computeFenced required the request memory to be moved into shared memory, block here until + // the fenced execution has completed and flush the memory back. + if (relocation.output) { + const auto state = resultSyncFence.syncWait({}); + if (state != nn::SyncFence::FenceState::SIGNALED) { + return NN_ERROR() << "syncWait failed with " << state; + } + relocation.output->flush(); + } + + // Create callback which can be used to retrieve the execution error status and timings. + nn::ExecuteFencedInfoCallback resultCallback = + [callback]() -> nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> { + ErrorStatus errorStatus; + Timing timingLaunched; + Timing timingFenced; + const auto ret = callback->getExecutionInfo(&timingLaunched, &timingFenced, &errorStatus); + HANDLE_ASTATUS(ret) << "fenced execution callback getExecutionInfo failed"; + return convertFencedExecutionResults(errorStatus, timingLaunched, timingFenced); + }; + + return std::make_pair(std::move(resultSyncFence), std::move(resultCallback)); +} + } // namespace nn::GeneralResult<std::shared_ptr<const PreparedModel>> PreparedModel::create( - std::shared_ptr<aidl_hal::IPreparedModel> preparedModel) { + std::shared_ptr<aidl_hal::IPreparedModel> preparedModel, nn::Version featureLevel) { if (preparedModel == nullptr) { return NN_ERROR() << "aidl_hal::utils::PreparedModel::create must have non-null preparedModel"; } - return std::make_shared<const PreparedModel>(PrivateConstructorTag{}, std::move(preparedModel)); + return std::make_shared<const PreparedModel>(PrivateConstructorTag{}, std::move(preparedModel), + featureLevel); } PreparedModel::PreparedModel(PrivateConstructorTag /*tag*/, - std::shared_ptr<aidl_hal::IPreparedModel> preparedModel) - : kPreparedModel(std::move(preparedModel)) {} + std::shared_ptr<aidl_hal::IPreparedModel> preparedModel, + nn::Version featureLevel) + : kPreparedModel(std::move(preparedModel)), kFeatureLevel(featureLevel) {} nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> PreparedModel::execute( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration) const { + const nn::OptionalTimePoint& deadline, const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { // Ensure that request is ready for IPC. std::optional<nn::Request> maybeRequestInShared; hal::utils::RequestRelocation relocation; - const nn::Request& requestInShared = - NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared( - &request, nn::kDefaultRequestMemoryAlignment, nn::kDefaultRequestMemoryPadding, - &maybeRequestInShared, &relocation))); - - const auto aidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared))); - const auto aidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure))); - const auto aidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline))); - const auto aidlLoopTimeoutDuration = - NN_TRY(hal::utils::makeExecutionFailure(convert(loopTimeoutDuration))); - return executeInternal(aidlRequest, aidlMeasure, aidlDeadline, aidlLoopTimeoutDuration, - relocation); + const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared( + &request, nn::kDefaultRequestMemoryAlignment, nn::kDefaultRequestMemoryPadding, + &maybeRequestInShared, &relocation)); + + const auto aidlRequest = NN_TRY(convert(requestInShared)); + const auto aidlMeasure = NN_TRY(convert(measure)); + const auto aidlDeadline = NN_TRY(convert(deadline)); + const auto aidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration)); + return executeInternal(aidlRequest, aidlMeasure, aidlDeadline, aidlLoopTimeoutDuration, hints, + extensionNameToPrefix, relocation); } nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> PreparedModel::executeInternal(const Request& request, bool measure, int64_t deadline, int64_t loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix, const hal::utils::RequestRelocation& relocation) const { if (relocation.input) { relocation.input->flush(); } ExecutionResult executionResult; - const auto ret = kPreparedModel->executeSynchronously(request, measure, deadline, - loopTimeoutDuration, &executionResult); - HANDLE_ASTATUS(ret) << "executeSynchronously failed"; - if (!executionResult.outputSufficientSize) { - auto canonicalOutputShapes = - nn::convert(executionResult.outputShapes).value_or(std::vector<nn::OutputShape>{}); - return NN_ERROR(nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, std::move(canonicalOutputShapes)) - << "execution failed with " << nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE; - } - auto [outputShapes, timing] = NN_TRY(hal::utils::makeExecutionFailure( - convertExecutionResults(executionResult.outputShapes, executionResult.timing))); - - if (relocation.output) { - relocation.output->flush(); + if (kFeatureLevel.level >= nn::Version::Level::FEATURE_LEVEL_8) { + auto aidlHints = NN_TRY(convert(hints)); + auto aidlExtensionPrefix = NN_TRY(convert(extensionNameToPrefix)); + const auto ret = kPreparedModel->executeSynchronouslyWithConfig( + request, + {measure, loopTimeoutDuration, std::move(aidlHints), + std::move(aidlExtensionPrefix)}, + deadline, &executionResult); + HANDLE_ASTATUS(ret) << "executeSynchronouslyWithConfig failed"; + } else { + const auto ret = kPreparedModel->executeSynchronously( + request, measure, deadline, loopTimeoutDuration, &executionResult); + HANDLE_ASTATUS(ret) << "executeSynchronously failed"; } - return std::make_pair(std::move(outputShapes), timing); + return handleExecutionResult(executionResult, relocation); } nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> -PreparedModel::executeFenced(const nn::Request& request, const std::vector<nn::SyncFence>& waitFor, - nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline, - const nn::OptionalDuration& loopTimeoutDuration, - const nn::OptionalDuration& timeoutDurationAfterFence) const { +PreparedModel::executeFenced( + const nn::Request& request, const std::vector<nn::SyncFence>& waitFor, + nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline, + const nn::OptionalDuration& loopTimeoutDuration, + const nn::OptionalDuration& timeoutDurationAfterFence, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { // Ensure that request is ready for IPC. std::optional<nn::Request> maybeRequestInShared; hal::utils::RequestRelocation relocation; @@ -138,63 +196,45 @@ PreparedModel::executeFenced(const nn::Request& request, const std::vector<nn::S const auto aidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration)); const auto aidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence)); return executeFencedInternal(aidlRequest, aidlWaitFor, aidlMeasure, aidlDeadline, - aidlLoopTimeoutDuration, aidlTimeoutDurationAfterFence, - relocation); + aidlLoopTimeoutDuration, aidlTimeoutDurationAfterFence, hints, + extensionNameToPrefix, relocation); } nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> -PreparedModel::executeFencedInternal(const Request& request, - const std::vector<ndk::ScopedFileDescriptor>& waitFor, - bool measure, int64_t deadline, int64_t loopTimeoutDuration, - int64_t timeoutDurationAfterFence, - const hal::utils::RequestRelocation& relocation) const { +PreparedModel::executeFencedInternal( + const Request& request, const std::vector<ndk::ScopedFileDescriptor>& waitFor, bool measure, + int64_t deadline, int64_t loopTimeoutDuration, int64_t timeoutDurationAfterFence, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix, + const hal::utils::RequestRelocation& relocation) const { if (relocation.input) { relocation.input->flush(); } FencedExecutionResult result; - const auto ret = - kPreparedModel->executeFenced(request, waitFor, measure, deadline, loopTimeoutDuration, - timeoutDurationAfterFence, &result); - HANDLE_ASTATUS(ret) << "executeFenced failed"; - - auto resultSyncFence = nn::SyncFence::createAsSignaled(); - if (result.syncFence.get() != -1) { - resultSyncFence = NN_TRY(nn::convert(result.syncFence)); + if (kFeatureLevel.level >= nn::Version::Level::FEATURE_LEVEL_8) { + auto aidlHints = NN_TRY(convert(hints)); + auto aidlExtensionPrefix = NN_TRY(convert(extensionNameToPrefix)); + const auto ret = kPreparedModel->executeFencedWithConfig( + request, waitFor, + {measure, loopTimeoutDuration, std::move(aidlHints), + std::move(aidlExtensionPrefix)}, + deadline, timeoutDurationAfterFence, &result); + HANDLE_ASTATUS(ret) << "executeFencedWithConfig failed"; + } else { + const auto ret = kPreparedModel->executeFenced(request, waitFor, measure, deadline, + loopTimeoutDuration, + timeoutDurationAfterFence, &result); + HANDLE_ASTATUS(ret) << "executeFenced failed"; } - - auto callback = result.callback; - if (callback == nullptr) { - return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "callback is null"; - } - - // If executeFenced required the request memory to be moved into shared memory, block here until - // the fenced execution has completed and flush the memory back. - if (relocation.output) { - const auto state = resultSyncFence.syncWait({}); - if (state != nn::SyncFence::FenceState::SIGNALED) { - return NN_ERROR() << "syncWait failed with " << state; - } - relocation.output->flush(); - } - - // Create callback which can be used to retrieve the execution error status and timings. - nn::ExecuteFencedInfoCallback resultCallback = - [callback]() -> nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> { - ErrorStatus errorStatus; - Timing timingLaunched; - Timing timingFenced; - const auto ret = callback->getExecutionInfo(&timingLaunched, &timingFenced, &errorStatus); - HANDLE_ASTATUS(ret) << "fenced execution callback getExecutionInfo failed"; - return convertFencedExecutionResults(errorStatus, timingLaunched, timingFenced); - }; - - return std::make_pair(std::move(resultSyncFence), std::move(resultCallback)); + return handleFencedExecutionResult(result, relocation); } nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution( const nn::Request& request, nn::MeasureTiming measure, - const nn::OptionalDuration& loopTimeoutDuration) const { + const nn::OptionalDuration& loopTimeoutDuration, + const std::vector<nn::TokenValuePair>& hints, + const std::vector<nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const { // Ensure that request is ready for IPC. std::optional<nn::Request> maybeRequestInShared; hal::utils::RequestRelocation relocation; @@ -205,15 +245,31 @@ nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution( auto aidlRequest = NN_TRY(convert(requestInShared)); auto aidlMeasure = NN_TRY(convert(measure)); auto aidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration)); - return Execution::create(shared_from_this(), std::move(aidlRequest), std::move(relocation), - aidlMeasure, aidlLoopTimeoutDuration); + + if (kFeatureLevel.level >= nn::Version::Level::FEATURE_LEVEL_8) { + std::shared_ptr<IExecution> execution; + auto aidlHints = NN_TRY(convert(hints)); + auto aidlExtensionPrefix = NN_TRY(convert(extensionNameToPrefix)); + + const auto ret = kPreparedModel->createReusableExecution( + aidlRequest, + {aidlMeasure, aidlLoopTimeoutDuration, std::move(aidlHints), + std::move(aidlExtensionPrefix)}, + &execution); + HANDLE_ASTATUS(ret) << "createReusableExecution failed"; + return Execution::create(std::move(execution), std::move(relocation)); + } + + return ExecutionWithCachedRequest::create(shared_from_this(), std::move(aidlRequest), + std::move(relocation), aidlMeasure, + aidlLoopTimeoutDuration); } nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const { std::shared_ptr<IBurst> burst; const auto ret = kPreparedModel->configureExecutionBurst(&burst); HANDLE_ASTATUS(ret) << "configureExecutionBurst failed"; - return Burst::create(std::move(burst)); + return Burst::create(std::move(burst), kFeatureLevel); } std::any PreparedModel::getUnderlyingResource() const { @@ -221,4 +277,36 @@ std::any PreparedModel::getUnderlyingResource() const { return resource; } +nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Execution::compute( + const nn::OptionalTimePoint& deadline) const { + const auto aidlDeadline = NN_TRY(convert(deadline)); + + if (kRelocation.input) { + kRelocation.input->flush(); + } + + ExecutionResult executionResult; + auto ret = kExecution->executeSynchronously(aidlDeadline, &executionResult); + HANDLE_ASTATUS(ret) << "executeSynchronously failed"; + return handleExecutionResult(executionResult, kRelocation); +} + +nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> Execution::computeFenced( + const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline, + const nn::OptionalDuration& timeoutDurationAfterFence) const { + const auto aidlWaitFor = NN_TRY(convert(waitFor)); + const auto aidlDeadline = NN_TRY(convert(deadline)); + const auto aidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence)); + + if (kRelocation.input) { + kRelocation.input->flush(); + } + + FencedExecutionResult result; + const auto ret = kExecution->executeFenced(aidlWaitFor, aidlDeadline, + aidlTimeoutDurationAfterFence, &result); + HANDLE_ASTATUS(ret) << "executeFenced failed"; + return handleFencedExecutionResult(result, kRelocation); +} + } // namespace aidl::android::hardware::neuralnetworks::utils diff --git a/neuralnetworks/aidl/utils/src/ProtectCallback.cpp b/neuralnetworks/aidl/utils/src/ProtectCallback.cpp index 124641cbb8..54a673caf5 100644 --- a/neuralnetworks/aidl/utils/src/ProtectCallback.cpp +++ b/neuralnetworks/aidl/utils/src/ProtectCallback.cpp @@ -22,7 +22,6 @@ #include <android/binder_auto_utils.h> #include <android/binder_interface_utils.h> #include <nnapi/Result.h> -#include <nnapi/hal/ProtectCallback.h> #include <algorithm> #include <functional> @@ -37,7 +36,7 @@ namespace aidl::android::hardware::neuralnetworks::utils { void DeathMonitor::serviceDied() { std::lock_guard guard(mMutex); std::for_each(mObjects.begin(), mObjects.end(), - [](hal::utils::IProtectedCallback* killable) { killable->notifyAsDeadObject(); }); + [](IProtectedCallback* killable) { killable->notifyAsDeadObject(); }); } void DeathMonitor::serviceDied(void* cookie) { @@ -45,13 +44,13 @@ void DeathMonitor::serviceDied(void* cookie) { deathMonitor->serviceDied(); } -void DeathMonitor::add(hal::utils::IProtectedCallback* killable) const { +void DeathMonitor::add(IProtectedCallback* killable) const { CHECK(killable != nullptr); std::lock_guard guard(mMutex); mObjects.push_back(killable); } -void DeathMonitor::remove(hal::utils::IProtectedCallback* killable) const { +void DeathMonitor::remove(IProtectedCallback* killable) const { CHECK(killable != nullptr); std::lock_guard guard(mMutex); const auto removedIter = std::remove(mObjects.begin(), mObjects.end(), killable); @@ -102,7 +101,7 @@ DeathHandler::~DeathHandler() { } [[nodiscard]] ::android::base::ScopeGuard<DeathHandler::Cleanup> DeathHandler::protectCallback( - hal::utils::IProtectedCallback* killable) const { + IProtectedCallback* killable) const { CHECK(killable != nullptr); kDeathMonitor->add(killable); return ::android::base::make_scope_guard( diff --git a/neuralnetworks/aidl/utils/src/Service.cpp b/neuralnetworks/aidl/utils/src/Service.cpp index ac182a205e..24fbb5309b 100644 --- a/neuralnetworks/aidl/utils/src/Service.cpp +++ b/neuralnetworks/aidl/utils/src/Service.cpp @@ -17,6 +17,7 @@ #include "Service.h" #include <AndroidVersionUtil.h> +#include <aidl/android/hardware/neuralnetworks/IDevice.h> #include <android/binder_auto_utils.h> #include <android/binder_manager.h> #include <android/binder_process.h> @@ -28,14 +29,38 @@ #include <string> #include "Device.h" +#include "Utils.h" namespace aidl::android::hardware::neuralnetworks::utils { +namespace { -nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& instanceName) { +// Map the AIDL version of an IDevice to NNAPI canonical feature level. +nn::GeneralResult<nn::Version> getAidlServiceFeatureLevel(IDevice* service) { + CHECK(service != nullptr); + int aidlVersion; + const auto ret = service->getInterfaceVersion(&aidlVersion); + HANDLE_ASTATUS(ret) << "getInterfaceVersion failed"; + + // For service AIDL versions greater than or equal to the AIDL library version that the runtime + // was built against, clamp it to the runtime AIDL library version. + aidlVersion = std::min(aidlVersion, IDevice::version); + + // Map stable AIDL versions to canonical versions. + auto version = aidlVersionToCanonicalVersion(aidlVersion); + if (!version.has_value()) { + return NN_ERROR() << "Unknown AIDL service version: " << aidlVersion; + } + return version.value(); +} + +} // namespace + +nn::GeneralResult<nn::SharedDevice> getDevice( + const std::string& instanceName, ::android::nn::Version::Level maxFeatureLevelAllowed) { auto fullName = std::string(IDevice::descriptor) + "/" + instanceName; hal::utils::ResilientDevice::Factory makeDevice = - [instanceName, - name = std::move(fullName)](bool blocking) -> nn::GeneralResult<nn::SharedDevice> { + [instanceName, name = std::move(fullName), + maxFeatureLevelAllowed](bool blocking) -> nn::GeneralResult<nn::SharedDevice> { std::add_pointer_t<AIBinder*(const char*)> getService; if (blocking) { if (__builtin_available(android __NNAPI_AIDL_MIN_ANDROID_API__, *)) { @@ -55,7 +80,9 @@ nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& instanceName) { << " returned nullptr"; } ABinderProcess_startThreadPool(); - return Device::create(instanceName, std::move(service)); + auto featureLevel = NN_TRY(getAidlServiceFeatureLevel(service.get())); + featureLevel.level = std::min(featureLevel.level, maxFeatureLevelAllowed); + return Device::create(instanceName, std::move(service), featureLevel); }; return hal::utils::ResilientDevice::create(std::move(makeDevice)); diff --git a/neuralnetworks/aidl/utils/src/Utils.cpp b/neuralnetworks/aidl/utils/src/Utils.cpp index 03407be4ce..f9b4f6edbc 100644 --- a/neuralnetworks/aidl/utils/src/Utils.cpp +++ b/neuralnetworks/aidl/utils/src/Utils.cpp @@ -51,8 +51,9 @@ nn::GeneralResult<ndk::ScopedFileDescriptor> clone(const ndk::ScopedFileDescript } nn::GeneralResult<common::NativeHandle> clone(const common::NativeHandle& handle) { + auto fds = NN_TRY(cloneVec(handle.fds)); return common::NativeHandle{ - .fds = NN_TRY(cloneVec(handle.fds)), + .fds = std::move(fds), .ints = handle.ints, }; } @@ -63,32 +64,35 @@ nn::GeneralResult<Memory> clone(const Memory& memory) { switch (memory.getTag()) { case Memory::Tag::ashmem: { const auto& ashmem = memory.get<Memory::Tag::ashmem>(); + auto fd = NN_TRY(clone(ashmem.fd)); auto handle = common::Ashmem{ - .fd = NN_TRY(clone(ashmem.fd)), + .fd = std::move(fd), .size = ashmem.size, }; return Memory::make<Memory::Tag::ashmem>(std::move(handle)); } case Memory::Tag::mappableFile: { const auto& memFd = memory.get<Memory::Tag::mappableFile>(); + auto fd = NN_TRY(clone(memFd.fd)); auto handle = common::MappableFile{ .length = memFd.length, .prot = memFd.prot, - .fd = NN_TRY(clone(memFd.fd)), + .fd = std::move(fd), .offset = memFd.offset, }; return Memory::make<Memory::Tag::mappableFile>(std::move(handle)); } case Memory::Tag::hardwareBuffer: { const auto& hardwareBuffer = memory.get<Memory::Tag::hardwareBuffer>(); - auto handle = graphics::common::HardwareBuffer{ + auto handle = NN_TRY(clone(hardwareBuffer.handle)); + auto ahwbHandle = graphics::common::HardwareBuffer{ .description = hardwareBuffer.description, - .handle = NN_TRY(clone(hardwareBuffer.handle)), + .handle = std::move(handle), }; - return Memory::make<Memory::Tag::hardwareBuffer>(std::move(handle)); + return Memory::make<Memory::Tag::hardwareBuffer>(std::move(ahwbHandle)); } } - return (NN_ERROR() << "Unrecognized Memory::Tag: " << memory.getTag()) + return (NN_ERROR() << "Unrecognized Memory::Tag: " << underlyingType(memory.getTag())) . operator nn::GeneralResult<Memory>(); } @@ -103,25 +107,27 @@ nn::GeneralResult<RequestMemoryPool> clone(const RequestMemoryPool& requestPool) } // Using explicit type conversion because std::variant inside the RequestMemoryPool confuses the // compiler. - return (NN_ERROR() << "Unrecognized request pool tag: " << requestPool.getTag()) + return (NN_ERROR() << "Unrecognized request pool tag: " << underlyingType(requestPool.getTag())) . operator nn::GeneralResult<RequestMemoryPool>(); } nn::GeneralResult<Request> clone(const Request& request) { + auto pools = NN_TRY(clone(request.pools)); return Request{ .inputs = request.inputs, .outputs = request.outputs, - .pools = NN_TRY(clone(request.pools)), + .pools = std::move(pools), }; } nn::GeneralResult<Model> clone(const Model& model) { + auto pools = NN_TRY(clone(model.pools)); return Model{ .main = model.main, .referenced = model.referenced, .operandValues = model.operandValues, - .pools = NN_TRY(clone(model.pools)), + .pools = std::move(pools), .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16, .extensionNameToPrefix = model.extensionNameToPrefix, }; diff --git a/neuralnetworks/aidl/utils/src/ValidateHal.cpp b/neuralnetworks/aidl/utils/src/ValidateHal.cpp new file mode 100644 index 0000000000..a56de21705 --- /dev/null +++ b/neuralnetworks/aidl/utils/src/ValidateHal.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2021 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 "ValidateHal" + +#include "ValidateHal.h" + +#include "HalUtils.h" + +#include <android-base/logging.h> +#include <nnapi/TypeUtils.h> +#include <nnapi/hal/aidl/Conversions.h> + +#include <algorithm> +#include <memory> +#include <set> +#include <utility> +#include <vector> + +namespace android { +namespace nn { + +bool validateMemoryDesc( + const aidl_hal::BufferDesc& desc, + const std::vector<std::shared_ptr<aidl_hal::IPreparedModel>>& preparedModels, + const std::vector<aidl_hal::BufferRole>& inputRoles, + const std::vector<aidl_hal::BufferRole>& outputRoles, + std::function<const aidl_hal::Model*(const std::shared_ptr<aidl_hal::IPreparedModel>&)> + getModel, + std::set<AidlHalPreparedModelRole>* preparedModelRoles, + aidl_hal::Operand* combinedOperand) { + NN_RET_CHECK(!preparedModels.empty()); + NN_RET_CHECK(!inputRoles.empty() || !outputRoles.empty()); + + std::set<AidlHalPreparedModelRole> roles; + std::vector<aidl_hal::Operand> operands; + operands.reserve(inputRoles.size() + outputRoles.size()); + for (const auto& role : inputRoles) { + NN_RET_CHECK_GE(role.modelIndex, 0); + NN_RET_CHECK_LT(static_cast<size_t>(role.modelIndex), preparedModels.size()); + const auto& preparedModel = preparedModels[role.modelIndex]; + NN_RET_CHECK(preparedModel != nullptr); + const auto* model = getModel(preparedModel); + NN_RET_CHECK(model != nullptr); + const auto& inputIndexes = model->main.inputIndexes; + NN_RET_CHECK_GE(role.ioIndex, 0); + NN_RET_CHECK_LT(static_cast<size_t>(role.ioIndex), inputIndexes.size()); + NN_RET_CHECK_GT(role.probability, 0.0f); + NN_RET_CHECK_LE(role.probability, 1.0f); + const auto [it, success] = roles.emplace(preparedModel.get(), IOType::INPUT, role.ioIndex); + NN_RET_CHECK(success); + operands.push_back(model->main.operands[inputIndexes[role.ioIndex]]); + } + for (const auto& role : outputRoles) { + NN_RET_CHECK_GE(role.modelIndex, 0); + NN_RET_CHECK_LT(static_cast<size_t>(role.modelIndex), preparedModels.size()); + const auto& preparedModel = preparedModels[role.modelIndex]; + NN_RET_CHECK(preparedModel != nullptr); + const auto* model = getModel(preparedModel); + NN_RET_CHECK(model != nullptr); + const auto& outputIndexes = model->main.outputIndexes; + NN_RET_CHECK_GE(role.ioIndex, 0); + NN_RET_CHECK_LT(static_cast<size_t>(role.ioIndex), outputIndexes.size()); + NN_RET_CHECK_GT(role.probability, 0.0f); + NN_RET_CHECK_LE(role.probability, 1.0f); + const auto [it, success] = roles.emplace(preparedModel.get(), IOType::OUTPUT, role.ioIndex); + NN_RET_CHECK(success); + operands.push_back(model->main.operands[outputIndexes[role.ioIndex]]); + } + + CHECK(!operands.empty()); + const auto opType = operands[0].type; + const auto canonicalOperandType = convert(opType); + NN_RET_CHECK(canonicalOperandType.has_value()) << canonicalOperandType.error().message; + const bool isExtensionOperand = isExtension(canonicalOperandType.value()); + + auto maybeDimensions = toUnsigned(desc.dimensions); + NN_RET_CHECK(maybeDimensions.has_value()) << maybeDimensions.error().message; + std::vector<uint32_t> dimensions = std::move(maybeDimensions).value(); + + for (const auto& operand : operands) { + NN_RET_CHECK(operand.type == operands[0].type) + << toString(operand.type) << " vs " << toString(operands[0].type); + NN_RET_CHECK_EQ(operand.scale, operands[0].scale); + NN_RET_CHECK_EQ(operand.zeroPoint, operands[0].zeroPoint); + // NOTE: validateMemoryDesc cannot validate extra parameters for extension operand type. + if (!isExtensionOperand) { + const auto& lhsExtraParams = operand.extraParams; + const auto& rhsExtraParams = operands[0].extraParams; + NN_RET_CHECK(lhsExtraParams == rhsExtraParams) + << (lhsExtraParams.has_value() ? lhsExtraParams.value().toString() + : "std::nullopt") + << " vs " + << (rhsExtraParams.has_value() ? rhsExtraParams.value().toString() + : "std::nullopt"); + } + const auto maybeRhsDimensions = toUnsigned(operand.dimensions); + NN_RET_CHECK(maybeRhsDimensions.has_value()) << maybeRhsDimensions.error().message; + const auto combined = combineDimensions(dimensions, maybeRhsDimensions.value()); + NN_RET_CHECK(combined.has_value()); + dimensions = combined.value(); + } + + // NOTE: validateMemoryDesc cannot validate scalar dimensions with extension operand type. + if (!isExtensionOperand) { + NN_RET_CHECK(!isNonExtensionScalar(opType) || dimensions.empty()) + << "invalid dimensions with scalar operand type."; + } + + if (preparedModelRoles != nullptr) { + *preparedModelRoles = std::move(roles); + } + if (combinedOperand != nullptr) { + *combinedOperand = operands[0]; + // No need to check that values fit int32_t here, since the original values are obtained + // from int32_t. + combinedOperand->dimensions = aidl_hal::utils::toSigned(dimensions).value(); + } + return true; +} + +} // namespace nn +} // namespace android |