summaryrefslogtreecommitdiff
path: root/neuralnetworks/aidl/utils/src
diff options
context:
space:
mode:
Diffstat (limited to 'neuralnetworks/aidl/utils/src')
-rw-r--r--neuralnetworks/aidl/utils/src/Buffer.cpp1
-rw-r--r--neuralnetworks/aidl/utils/src/BufferTracker.cpp227
-rw-r--r--neuralnetworks/aidl/utils/src/Burst.cpp91
-rw-r--r--neuralnetworks/aidl/utils/src/Callbacks.cpp12
-rw-r--r--neuralnetworks/aidl/utils/src/Conversions.cpp515
-rw-r--r--neuralnetworks/aidl/utils/src/Device.cpp40
-rw-r--r--neuralnetworks/aidl/utils/src/Execution.cpp56
-rw-r--r--neuralnetworks/aidl/utils/src/HalUtils.cpp72
-rw-r--r--neuralnetworks/aidl/utils/src/InvalidDevice.cpp26
-rw-r--r--neuralnetworks/aidl/utils/src/PreparedModel.cpp260
-rw-r--r--neuralnetworks/aidl/utils/src/ProtectCallback.cpp9
-rw-r--r--neuralnetworks/aidl/utils/src/Service.cpp35
-rw-r--r--neuralnetworks/aidl/utils/src/Utils.cpp26
-rw-r--r--neuralnetworks/aidl/utils/src/ValidateHal.cpp136
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