Merge changes from topic "nnapi-numberOfConsumers"
* changes: Create NNAPI adapter interface Make NNAPI countNumberOfConsumers return GeneralResult -- hal
This commit is contained in:
commit
65de531533
16 changed files with 1449 additions and 15 deletions
|
@ -162,7 +162,7 @@ GeneralResult<Model> unvalidatedConvert(const hal::V1_0::Model& model) {
|
|||
|
||||
// Verify number of consumers.
|
||||
const auto numberOfConsumers =
|
||||
hal::utils::countNumberOfConsumers(model.operands.size(), operations);
|
||||
NN_TRY(hal::utils::countNumberOfConsumers(model.operands.size(), operations));
|
||||
CHECK(model.operands.size() == numberOfConsumers.size());
|
||||
for (size_t i = 0; i < model.operands.size(); ++i) {
|
||||
if (model.operands[i].numberOfConsumers != numberOfConsumers[i]) {
|
||||
|
@ -360,7 +360,7 @@ nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
|
|||
|
||||
// Update number of consumers.
|
||||
const auto numberOfConsumers =
|
||||
hal::utils::countNumberOfConsumers(operands.size(), model.main.operations);
|
||||
NN_TRY(hal::utils::countNumberOfConsumers(operands.size(), model.main.operations));
|
||||
CHECK(operands.size() == numberOfConsumers.size());
|
||||
for (size_t i = 0; i < operands.size(); ++i) {
|
||||
operands[i].numberOfConsumers = numberOfConsumers[i];
|
||||
|
|
|
@ -111,7 +111,7 @@ GeneralResult<Model> unvalidatedConvert(const hal::V1_1::Model& model) {
|
|||
|
||||
// Verify number of consumers.
|
||||
const auto numberOfConsumers =
|
||||
hal::utils::countNumberOfConsumers(model.operands.size(), operations);
|
||||
NN_TRY(hal::utils::countNumberOfConsumers(model.operands.size(), operations));
|
||||
CHECK(model.operands.size() == numberOfConsumers.size());
|
||||
for (size_t i = 0; i < model.operands.size(); ++i) {
|
||||
if (model.operands[i].numberOfConsumers != numberOfConsumers[i]) {
|
||||
|
@ -241,7 +241,7 @@ nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
|
|||
|
||||
// Update number of consumers.
|
||||
const auto numberOfConsumers =
|
||||
hal::utils::countNumberOfConsumers(operands.size(), model.main.operations);
|
||||
NN_TRY(hal::utils::countNumberOfConsumers(operands.size(), model.main.operations));
|
||||
CHECK(operands.size() == numberOfConsumers.size());
|
||||
for (size_t i = 0; i < operands.size(); ++i) {
|
||||
operands[i].numberOfConsumers = numberOfConsumers[i];
|
||||
|
|
|
@ -227,7 +227,7 @@ GeneralResult<Model> unvalidatedConvert(const hal::V1_2::Model& model) {
|
|||
|
||||
// Verify number of consumers.
|
||||
const auto numberOfConsumers =
|
||||
hal::utils::countNumberOfConsumers(model.operands.size(), operations);
|
||||
NN_TRY(hal::utils::countNumberOfConsumers(model.operands.size(), operations));
|
||||
CHECK(model.operands.size() == numberOfConsumers.size());
|
||||
for (size_t i = 0; i < model.operands.size(); ++i) {
|
||||
if (model.operands[i].numberOfConsumers != numberOfConsumers[i]) {
|
||||
|
@ -529,7 +529,7 @@ nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
|
|||
|
||||
// Update number of consumers.
|
||||
const auto numberOfConsumers =
|
||||
hal::utils::countNumberOfConsumers(operands.size(), model.main.operations);
|
||||
NN_TRY(hal::utils::countNumberOfConsumers(operands.size(), model.main.operations));
|
||||
CHECK(operands.size() == numberOfConsumers.size());
|
||||
for (size_t i = 0; i < operands.size(); ++i) {
|
||||
operands[i].numberOfConsumers = numberOfConsumers[i];
|
||||
|
|
|
@ -217,7 +217,7 @@ GeneralResult<Model::Subgraph> unvalidatedConvert(const hal::V1_3::Subgraph& sub
|
|||
|
||||
// Verify number of consumers.
|
||||
const auto numberOfConsumers =
|
||||
hal::utils::countNumberOfConsumers(subgraph.operands.size(), operations);
|
||||
NN_TRY(hal::utils::countNumberOfConsumers(subgraph.operands.size(), operations));
|
||||
CHECK(subgraph.operands.size() == numberOfConsumers.size());
|
||||
for (size_t i = 0; i < subgraph.operands.size(); ++i) {
|
||||
if (subgraph.operands[i].numberOfConsumers != numberOfConsumers[i]) {
|
||||
|
@ -559,7 +559,7 @@ nn::GeneralResult<Subgraph> unvalidatedConvert(const nn::Model::Subgraph& subgra
|
|||
|
||||
// Update number of consumers.
|
||||
const auto numberOfConsumers =
|
||||
hal::utils::countNumberOfConsumers(operands.size(), subgraph.operations);
|
||||
NN_TRY(hal::utils::countNumberOfConsumers(operands.size(), subgraph.operations));
|
||||
CHECK(operands.size() == numberOfConsumers.size());
|
||||
for (size_t i = 0; i < operands.size(); ++i) {
|
||||
operands[i].numberOfConsumers = numberOfConsumers[i];
|
||||
|
|
|
@ -1310,8 +1310,10 @@ static void mutateExecutionPriorityTest(const std::shared_ptr<IDevice>& device,
|
|||
////////////////////////// ENTRY POINT //////////////////////////////
|
||||
|
||||
void validateModel(const std::shared_ptr<IDevice>& device, const Model& model) {
|
||||
const auto numberOfConsumers = nn::countNumberOfConsumers(
|
||||
model.main.operands.size(), nn::convert(model.main.operations).value());
|
||||
const auto numberOfConsumers =
|
||||
nn::countNumberOfConsumers(model.main.operands.size(),
|
||||
nn::convert(model.main.operations).value())
|
||||
.value();
|
||||
mutateExecutionOrderTest(device, model, numberOfConsumers);
|
||||
mutateOperandTypeTest(device, model);
|
||||
mutateOperandRankTest(device, model);
|
||||
|
|
37
neuralnetworks/utils/adapter/Android.bp
Normal file
37
neuralnetworks/utils/adapter/Android.bp
Normal file
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
cc_library_static {
|
||||
name: "neuralnetworks_utils_hal_adapter",
|
||||
defaults: ["neuralnetworks_utils_defaults"],
|
||||
srcs: ["src/*"],
|
||||
local_include_dirs: ["include/nnapi/hal"],
|
||||
export_include_dirs: ["include"],
|
||||
static_libs: [
|
||||
"neuralnetworks_types",
|
||||
"neuralnetworks_utils_hal_1_0",
|
||||
"neuralnetworks_utils_hal_1_1",
|
||||
"neuralnetworks_utils_hal_1_2",
|
||||
"neuralnetworks_utils_hal_1_3",
|
||||
],
|
||||
shared_libs: [
|
||||
"android.hardware.neuralnetworks@1.0",
|
||||
"android.hardware.neuralnetworks@1.1",
|
||||
"android.hardware.neuralnetworks@1.2",
|
||||
"android.hardware.neuralnetworks@1.3",
|
||||
"libfmq",
|
||||
],
|
||||
}
|
72
neuralnetworks/utils/adapter/include/nnapi/hal/Adapter.h
Normal file
72
neuralnetworks/utils/adapter/include/nnapi/hal/Adapter.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_ADAPTER_H
|
||||
#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_ADAPTER_H
|
||||
|
||||
#include <android/hardware/neuralnetworks/1.3/IDevice.h>
|
||||
#include <nnapi/IDevice.h>
|
||||
#include <nnapi/Types.h>
|
||||
#include <sys/types.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
|
||||
// lifetimes across processes and for protecting asynchronous calls across HIDL.
|
||||
|
||||
namespace android::hardware::neuralnetworks::adapter {
|
||||
|
||||
/**
|
||||
* A self-contained unit of work to be executed.
|
||||
*/
|
||||
using Task = std::function<void()>;
|
||||
|
||||
/**
|
||||
* A type-erased executor which executes a task asynchronously.
|
||||
*
|
||||
* This executor is also provided with an Application ID (Android User ID) and an optional deadline
|
||||
* for when the caller expects is the upper bound for the amount of time to complete the task.
|
||||
*/
|
||||
using Executor = std::function<void(Task, uid_t, nn::OptionalTimePoint)>;
|
||||
|
||||
/**
|
||||
* Adapt an NNAPI canonical interface object to a HIDL NN HAL interface object.
|
||||
*
|
||||
* The IPreparedModel object created from IDevice::prepareModel or IDevice::preparedModelFromCache
|
||||
* must return "const nn::Model*" from IPreparedModel::getUnderlyingResource().
|
||||
*
|
||||
* @param device NNAPI canonical IDevice interface object to be adapted.
|
||||
* @param executor Type-erased executor to handle executing tasks asynchronously.
|
||||
* @return HIDL NN HAL IDevice interface object.
|
||||
*/
|
||||
sp<V1_3::IDevice> adapt(nn::SharedDevice device, Executor executor);
|
||||
|
||||
/**
|
||||
* Adapt an NNAPI canonical interface object to a HIDL NN HAL interface object.
|
||||
*
|
||||
* The IPreparedModel object created from IDevice::prepareModel or IDevice::preparedModelFromCache
|
||||
* must return "const nn::Model*" from IPreparedModel::getUnderlyingResource().
|
||||
*
|
||||
* This function uses a default executor, which will execute tasks from a detached thread.
|
||||
*
|
||||
* @param device NNAPI canonical IDevice interface object to be adapted.
|
||||
* @return HIDL NN HAL IDevice interface object.
|
||||
*/
|
||||
sp<V1_3::IDevice> adapt(nn::SharedDevice device);
|
||||
|
||||
} // namespace android::hardware::neuralnetworks::adapter
|
||||
|
||||
#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_ADAPTER_H
|
46
neuralnetworks/utils/adapter/include/nnapi/hal/Buffer.h
Normal file
46
neuralnetworks/utils/adapter/include/nnapi/hal/Buffer.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_BUFFER_H
|
||||
#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_BUFFER_H
|
||||
|
||||
#include <android/hardware/neuralnetworks/1.3/IBuffer.h>
|
||||
#include <android/hardware/neuralnetworks/1.3/types.h>
|
||||
#include <nnapi/IBuffer.h>
|
||||
#include <nnapi/Types.h>
|
||||
#include <memory>
|
||||
|
||||
// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
|
||||
// lifetimes across processes and for protecting asynchronous calls across HIDL.
|
||||
|
||||
namespace android::hardware::neuralnetworks::adapter {
|
||||
|
||||
// Class that adapts nn::IBuffer to V1_3::IBuffer.
|
||||
class Buffer final : public V1_3::IBuffer {
|
||||
public:
|
||||
explicit Buffer(nn::SharedBuffer buffer);
|
||||
|
||||
Return<V1_3::ErrorStatus> copyTo(const hidl_memory& dst) override;
|
||||
Return<V1_3::ErrorStatus> copyFrom(const hidl_memory& src,
|
||||
const hidl_vec<uint32_t>& dimensions) override;
|
||||
|
||||
private:
|
||||
const nn::SharedBuffer kBuffer;
|
||||
};
|
||||
|
||||
} // namespace android::hardware::neuralnetworks::adapter
|
||||
|
||||
#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_BUFFER_H
|
96
neuralnetworks/utils/adapter/include/nnapi/hal/Device.h
Normal file
96
neuralnetworks/utils/adapter/include/nnapi/hal/Device.h
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_DEVICE_H
|
||||
#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_DEVICE_H
|
||||
|
||||
#include "nnapi/hal/Adapter.h"
|
||||
|
||||
#include <android/hardware/neuralnetworks/1.0/IPreparedModelCallback.h>
|
||||
#include <android/hardware/neuralnetworks/1.0/types.h>
|
||||
#include <android/hardware/neuralnetworks/1.1/types.h>
|
||||
#include <android/hardware/neuralnetworks/1.2/IPreparedModelCallback.h>
|
||||
#include <android/hardware/neuralnetworks/1.2/types.h>
|
||||
#include <android/hardware/neuralnetworks/1.3/IDevice.h>
|
||||
#include <android/hardware/neuralnetworks/1.3/IPreparedModelCallback.h>
|
||||
#include <android/hardware/neuralnetworks/1.3/types.h>
|
||||
#include <nnapi/IDevice.h>
|
||||
#include <nnapi/Types.h>
|
||||
#include <memory>
|
||||
|
||||
// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
|
||||
// lifetimes across processes and for protecting asynchronous calls across HIDL.
|
||||
|
||||
namespace android::hardware::neuralnetworks::adapter {
|
||||
|
||||
using CacheToken = hidl_array<uint8_t, nn::kByteSizeOfCacheToken>;
|
||||
|
||||
// Class that adapts nn::IDevice to V1_3::IDevice.
|
||||
class Device final : public V1_3::IDevice {
|
||||
public:
|
||||
Device(nn::SharedDevice device, Executor executor);
|
||||
|
||||
Return<void> getCapabilities(getCapabilities_cb cb) override;
|
||||
Return<void> getCapabilities_1_1(getCapabilities_1_1_cb cb) override;
|
||||
Return<void> getCapabilities_1_2(getCapabilities_1_2_cb cb) override;
|
||||
Return<void> getCapabilities_1_3(getCapabilities_1_3_cb cb) override;
|
||||
Return<void> getVersionString(getVersionString_cb cb) override;
|
||||
Return<void> getType(getType_cb cb) override;
|
||||
Return<void> getSupportedExtensions(getSupportedExtensions_cb) override;
|
||||
Return<void> getSupportedOperations(const V1_0::Model& model,
|
||||
getSupportedOperations_cb cb) override;
|
||||
Return<void> getSupportedOperations_1_1(const V1_1::Model& model,
|
||||
getSupportedOperations_1_1_cb cb) override;
|
||||
Return<void> getSupportedOperations_1_2(const V1_2::Model& model,
|
||||
getSupportedOperations_1_2_cb cb) override;
|
||||
Return<void> getSupportedOperations_1_3(const V1_3::Model& model,
|
||||
getSupportedOperations_1_3_cb cb) override;
|
||||
Return<void> getNumberOfCacheFilesNeeded(getNumberOfCacheFilesNeeded_cb cb) override;
|
||||
Return<V1_0::ErrorStatus> prepareModel(
|
||||
const V1_0::Model& model, const sp<V1_0::IPreparedModelCallback>& callback) override;
|
||||
Return<V1_0::ErrorStatus> prepareModel_1_1(
|
||||
const V1_1::Model& model, V1_1::ExecutionPreference preference,
|
||||
const sp<V1_0::IPreparedModelCallback>& callback) override;
|
||||
Return<V1_0::ErrorStatus> prepareModel_1_2(
|
||||
const V1_2::Model& model, V1_1::ExecutionPreference preference,
|
||||
const hidl_vec<hidl_handle>& modelCache, const hidl_vec<hidl_handle>& dataCache,
|
||||
const CacheToken& token, const sp<V1_2::IPreparedModelCallback>& callback) override;
|
||||
Return<V1_3::ErrorStatus> prepareModel_1_3(
|
||||
const V1_3::Model& model, V1_1::ExecutionPreference preference, V1_3::Priority priority,
|
||||
const V1_3::OptionalTimePoint& deadline, const hidl_vec<hidl_handle>& modelCache,
|
||||
const hidl_vec<hidl_handle>& dataCache, const CacheToken& token,
|
||||
const sp<V1_3::IPreparedModelCallback>& callback) override;
|
||||
Return<V1_0::ErrorStatus> prepareModelFromCache(
|
||||
const hidl_vec<hidl_handle>& modelCache, const hidl_vec<hidl_handle>& dataCache,
|
||||
const CacheToken& token, const sp<V1_2::IPreparedModelCallback>& callback) override;
|
||||
Return<V1_3::ErrorStatus> prepareModelFromCache_1_3(
|
||||
const V1_3::OptionalTimePoint& deadline, const hidl_vec<hidl_handle>& modelCache,
|
||||
const hidl_vec<hidl_handle>& dataCache, const CacheToken& token,
|
||||
const sp<V1_3::IPreparedModelCallback>& callback) override;
|
||||
Return<V1_0::DeviceStatus> getStatus() override;
|
||||
Return<void> allocate(const V1_3::BufferDesc& desc,
|
||||
const hidl_vec<sp<V1_3::IPreparedModel>>& preparedModels,
|
||||
const hidl_vec<V1_3::BufferRole>& inputRoles,
|
||||
const hidl_vec<V1_3::BufferRole>& outputRoles, allocate_cb cb) override;
|
||||
|
||||
private:
|
||||
const nn::SharedDevice kDevice;
|
||||
const Executor kExecutor;
|
||||
};
|
||||
|
||||
} // namespace android::hardware::neuralnetworks::adapter
|
||||
|
||||
#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_DEVICE_H
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_PREPARED_MODEL_H
|
||||
#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_PREPARED_MODEL_H
|
||||
|
||||
#include "nnapi/hal/Adapter.h"
|
||||
|
||||
#include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
|
||||
#include <android/hardware/neuralnetworks/1.0/types.h>
|
||||
#include <android/hardware/neuralnetworks/1.2/IBurstCallback.h>
|
||||
#include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
|
||||
#include <android/hardware/neuralnetworks/1.2/types.h>
|
||||
#include <android/hardware/neuralnetworks/1.3/IExecutionCallback.h>
|
||||
#include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
|
||||
#include <android/hardware/neuralnetworks/1.3/types.h>
|
||||
#include <nnapi/IPreparedModel.h>
|
||||
#include <nnapi/Types.h>
|
||||
#include <memory>
|
||||
|
||||
// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
|
||||
// lifetimes across processes and for protecting asynchronous calls across HIDL.
|
||||
|
||||
namespace android::hardware::neuralnetworks::adapter {
|
||||
|
||||
// Class that adapts nn::IPreparedModel to V1_3::IPreparedModel.
|
||||
class PreparedModel final : public V1_3::IPreparedModel {
|
||||
public:
|
||||
PreparedModel(nn::SharedPreparedModel preparedModel, Executor executor, uid_t userId);
|
||||
|
||||
Return<V1_0::ErrorStatus> execute(const V1_0::Request& request,
|
||||
const sp<V1_0::IExecutionCallback>& callback) override;
|
||||
Return<V1_0::ErrorStatus> execute_1_2(const V1_0::Request& request, V1_2::MeasureTiming measure,
|
||||
const sp<V1_2::IExecutionCallback>& callback) override;
|
||||
Return<V1_3::ErrorStatus> execute_1_3(const V1_3::Request& request, V1_2::MeasureTiming measure,
|
||||
const V1_3::OptionalTimePoint& deadline,
|
||||
const V1_3::OptionalTimeoutDuration& loopTimeoutDuration,
|
||||
const sp<V1_3::IExecutionCallback>& callback) override;
|
||||
Return<void> executeSynchronously(const V1_0::Request& request, V1_2::MeasureTiming measure,
|
||||
executeSynchronously_cb cb) override;
|
||||
Return<void> executeSynchronously_1_3(const V1_3::Request& request, V1_2::MeasureTiming measure,
|
||||
const V1_3::OptionalTimePoint& deadline,
|
||||
const V1_3::OptionalTimeoutDuration& loopTimeoutDuration,
|
||||
executeSynchronously_1_3_cb cb) override;
|
||||
Return<void> configureExecutionBurst(
|
||||
const sp<V1_2::IBurstCallback>& callback,
|
||||
const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
|
||||
const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
|
||||
configureExecutionBurst_cb cb) override;
|
||||
Return<void> executeFenced(const V1_3::Request& request, const hidl_vec<hidl_handle>& waitFor,
|
||||
V1_2::MeasureTiming measure, const V1_3::OptionalTimePoint& deadline,
|
||||
const V1_3::OptionalTimeoutDuration& loopTimeoutDuration,
|
||||
const V1_3::OptionalTimeoutDuration& duration,
|
||||
executeFenced_cb callback) override;
|
||||
|
||||
nn::SharedPreparedModel getUnderlyingPreparedModel() const;
|
||||
|
||||
private:
|
||||
const nn::SharedPreparedModel kPreparedModel;
|
||||
const Executor kExecutor;
|
||||
const uid_t kUserId;
|
||||
};
|
||||
|
||||
} // namespace android::hardware::neuralnetworks::adapter
|
||||
|
||||
#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_PREPARED_MODEL_H
|
46
neuralnetworks/utils/adapter/src/Adapter.cpp
Normal file
46
neuralnetworks/utils/adapter/src/Adapter.cpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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 "Adapter.h"
|
||||
|
||||
#include "Device.h"
|
||||
|
||||
#include <android/hardware/neuralnetworks/1.3/IDevice.h>
|
||||
#include <nnapi/IDevice.h>
|
||||
#include <nnapi/Types.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
|
||||
// lifetimes across processes and for protecting asynchronous calls across HIDL.
|
||||
|
||||
namespace android::hardware::neuralnetworks::adapter {
|
||||
|
||||
sp<V1_3::IDevice> adapt(nn::SharedDevice device, Executor executor) {
|
||||
return sp<Device>::make(std::move(device), std::move(executor));
|
||||
}
|
||||
|
||||
sp<V1_3::IDevice> adapt(nn::SharedDevice device) {
|
||||
Executor defaultExecutor = [](Task task, uid_t /*uid*/, nn::OptionalTimePoint /*deadline*/) {
|
||||
std::thread(std::move(task)).detach();
|
||||
};
|
||||
return adapt(std::move(device), std::move(defaultExecutor));
|
||||
}
|
||||
|
||||
} // namespace android::hardware::neuralnetworks::adapter
|
83
neuralnetworks/utils/adapter/src/Buffer.cpp
Normal file
83
neuralnetworks/utils/adapter/src/Buffer.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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 "Buffer.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android/hardware/neuralnetworks/1.3/IBuffer.h>
|
||||
#include <android/hardware/neuralnetworks/1.3/types.h>
|
||||
#include <nnapi/IBuffer.h>
|
||||
#include <nnapi/TypeUtils.h>
|
||||
#include <nnapi/Types.h>
|
||||
#include <nnapi/hal/1.3/Utils.h>
|
||||
#include <memory>
|
||||
|
||||
// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
|
||||
// lifetimes across processes and for protecting asynchronous calls across HIDL.
|
||||
|
||||
namespace android::hardware::neuralnetworks::adapter {
|
||||
namespace {
|
||||
|
||||
template <typename Type>
|
||||
auto convertInput(const Type& object) -> decltype(nn::convert(std::declval<Type>())) {
|
||||
auto result = nn::convert(object);
|
||||
if (!result.has_value()) {
|
||||
result.error().code = nn::ErrorStatus::INVALID_ARGUMENT;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
nn::GeneralResult<void> copyTo(const nn::SharedBuffer& buffer, const hidl_memory& dst) {
|
||||
const auto memory = NN_TRY(convertInput(dst));
|
||||
NN_TRY(buffer->copyTo(memory));
|
||||
return {};
|
||||
}
|
||||
|
||||
nn::GeneralResult<void> copyFrom(const nn::SharedBuffer& buffer, const hidl_memory& src,
|
||||
const hidl_vec<uint32_t>& dimensions) {
|
||||
const auto memory = NN_TRY(convertInput(src));
|
||||
NN_TRY(buffer->copyFrom(memory, dimensions));
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Buffer::Buffer(nn::SharedBuffer buffer) : kBuffer(std::move(buffer)) {
|
||||
CHECK(kBuffer != nullptr);
|
||||
}
|
||||
|
||||
Return<V1_3::ErrorStatus> Buffer::copyTo(const hidl_memory& dst) {
|
||||
auto result = adapter::copyTo(kBuffer, dst);
|
||||
if (!result.has_value()) {
|
||||
const auto [message, code] = std::move(result).error();
|
||||
LOG(ERROR) << "adapter::Buffer::copyTo failed with " << code << ": " << message;
|
||||
return V1_3::utils::convert(code).value();
|
||||
}
|
||||
return V1_3::ErrorStatus::NONE;
|
||||
}
|
||||
|
||||
Return<V1_3::ErrorStatus> Buffer::copyFrom(const hidl_memory& src,
|
||||
const hidl_vec<uint32_t>& dimensions) {
|
||||
auto result = adapter::copyFrom(kBuffer, src, dimensions);
|
||||
if (!result.has_value()) {
|
||||
const auto [message, code] = std::move(result).error();
|
||||
LOG(ERROR) << "adapter::Buffer::copyFrom failed with " << code << ": " << message;
|
||||
return V1_3::utils::convert(code).value();
|
||||
}
|
||||
return V1_3::ErrorStatus::NONE;
|
||||
}
|
||||
|
||||
} // namespace android::hardware::neuralnetworks::adapter
|
556
neuralnetworks/utils/adapter/src/Device.cpp
Normal file
556
neuralnetworks/utils/adapter/src/Device.cpp
Normal file
|
@ -0,0 +1,556 @@
|
|||
/*
|
||||
* 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 "Device.h"
|
||||
|
||||
#include "Buffer.h"
|
||||
#include "PreparedModel.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android/hardware/neuralnetworks/1.0/IPreparedModelCallback.h>
|
||||
#include <android/hardware/neuralnetworks/1.0/types.h>
|
||||
#include <android/hardware/neuralnetworks/1.1/types.h>
|
||||
#include <android/hardware/neuralnetworks/1.2/IPreparedModelCallback.h>
|
||||
#include <android/hardware/neuralnetworks/1.2/types.h>
|
||||
#include <android/hardware/neuralnetworks/1.3/IDevice.h>
|
||||
#include <android/hardware/neuralnetworks/1.3/IPreparedModelCallback.h>
|
||||
#include <android/hardware/neuralnetworks/1.3/types.h>
|
||||
#include <hwbinder/IPCThreadState.h>
|
||||
#include <nnapi/IBuffer.h>
|
||||
#include <nnapi/IDevice.h>
|
||||
#include <nnapi/IPreparedModel.h>
|
||||
#include <nnapi/Result.h>
|
||||
#include <nnapi/TypeUtils.h>
|
||||
#include <nnapi/Types.h>
|
||||
#include <nnapi/hal/1.0/Conversions.h>
|
||||
#include <nnapi/hal/1.0/Utils.h>
|
||||
#include <nnapi/hal/1.1/Conversions.h>
|
||||
#include <nnapi/hal/1.1/Utils.h>
|
||||
#include <nnapi/hal/1.2/Conversions.h>
|
||||
#include <nnapi/hal/1.2/Utils.h>
|
||||
#include <nnapi/hal/1.3/Conversions.h>
|
||||
#include <nnapi/hal/1.3/Utils.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
|
||||
// lifetimes across processes and for protecting asynchronous calls across HIDL.
|
||||
|
||||
namespace android::hardware::neuralnetworks::adapter {
|
||||
namespace {
|
||||
|
||||
template <typename Type>
|
||||
auto convertInput(const Type& object) -> decltype(nn::convert(std::declval<Type>())) {
|
||||
auto result = nn::convert(object);
|
||||
if (!result.has_value()) {
|
||||
result.error().code = nn::ErrorStatus::INVALID_ARGUMENT;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
using PrepareModelResult = nn::GeneralResult<nn::SharedPreparedModel>;
|
||||
|
||||
sp<PreparedModel> adaptPreparedModel(nn::SharedPreparedModel preparedModel, Executor executor,
|
||||
uid_t userId) {
|
||||
if (preparedModel == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return sp<PreparedModel>::make(std::move(preparedModel), std::move(executor), userId);
|
||||
}
|
||||
|
||||
void notify(V1_0::IPreparedModelCallback* callback, nn::ErrorStatus status,
|
||||
const sp<PreparedModel>& hidlPreparedModel) {
|
||||
if (callback != nullptr) {
|
||||
const auto hidlStatus = V1_0::utils::convert(status).value();
|
||||
const auto ret = callback->notify(hidlStatus, hidlPreparedModel);
|
||||
if (!ret.isOk()) {
|
||||
LOG(ERROR) << "V1_0::IPreparedModelCallback::notify failed with " << ret.description();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void notify(V1_2::IPreparedModelCallback* callback, nn::ErrorStatus status,
|
||||
const sp<PreparedModel>& hidlPreparedModel) {
|
||||
if (callback != nullptr) {
|
||||
const auto hidlStatus = V1_2::utils::convert(status).value();
|
||||
const auto ret = callback->notify_1_2(hidlStatus, hidlPreparedModel);
|
||||
if (!ret.isOk()) {
|
||||
LOG(ERROR) << "V1_2::IPreparedModelCallback::notify_1_2 failed with "
|
||||
<< ret.description();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void notify(V1_3::IPreparedModelCallback* callback, nn::ErrorStatus status,
|
||||
const sp<PreparedModel>& hidlPreparedModel) {
|
||||
if (callback != nullptr) {
|
||||
const auto hidlStatus = V1_3::utils::convert(status).value();
|
||||
const auto ret = callback->notify_1_3(hidlStatus, hidlPreparedModel);
|
||||
if (!ret.isOk()) {
|
||||
LOG(ERROR) << "V1_3::IPreparedModelCallback::notify_1_3 failed with "
|
||||
<< ret.description();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename CallbackType>
|
||||
void notify(CallbackType* callback, PrepareModelResult result, Executor executor, uid_t userId) {
|
||||
if (!result.has_value()) {
|
||||
const auto [message, status] = std::move(result).error();
|
||||
LOG(ERROR) << message;
|
||||
notify(callback, status, nullptr);
|
||||
} else {
|
||||
auto preparedModel = std::move(result).value();
|
||||
auto hidlPreparedModel =
|
||||
adaptPreparedModel(std::move(preparedModel), std::move(executor), userId);
|
||||
notify(callback, nn::ErrorStatus::NONE, std::move(hidlPreparedModel));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ModelType>
|
||||
nn::GeneralResult<hidl_vec<bool>> getSupportedOperations(const nn::SharedDevice& device,
|
||||
const ModelType& model) {
|
||||
const auto nnModel = NN_TRY(convertInput(model));
|
||||
return NN_TRY(device->getSupportedOperations(nnModel));
|
||||
}
|
||||
|
||||
nn::GeneralResult<void> prepareModel(const nn::SharedDevice& device, const Executor& executor,
|
||||
const V1_0::Model& model,
|
||||
const sp<V1_0::IPreparedModelCallback>& callback) {
|
||||
if (callback.get() == nullptr) {
|
||||
return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback";
|
||||
}
|
||||
|
||||
auto nnModel = NN_TRY(convertInput(model));
|
||||
|
||||
const uid_t userId = hardware::IPCThreadState::self()->getCallingUid();
|
||||
Task task = [device, nnModel = std::move(nnModel), userId, executor, callback] {
|
||||
auto result = device->prepareModel(nnModel, nn::ExecutionPreference::DEFAULT,
|
||||
nn::Priority::DEFAULT, {}, {}, {}, {});
|
||||
notify(callback.get(), std::move(result), executor, userId);
|
||||
};
|
||||
executor(std::move(task), userId, {});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
nn::GeneralResult<void> prepareModel_1_1(const nn::SharedDevice& device, const Executor& executor,
|
||||
const V1_1::Model& model,
|
||||
V1_1::ExecutionPreference preference,
|
||||
const sp<V1_0::IPreparedModelCallback>& callback) {
|
||||
if (callback.get() == nullptr) {
|
||||
return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback";
|
||||
}
|
||||
|
||||
auto nnModel = NN_TRY(convertInput(model));
|
||||
const auto nnPreference = NN_TRY(convertInput(preference));
|
||||
|
||||
const uid_t userId = hardware::IPCThreadState::self()->getCallingUid();
|
||||
Task task = [device, nnModel = std::move(nnModel), nnPreference, userId, executor, callback] {
|
||||
auto result =
|
||||
device->prepareModel(nnModel, nnPreference, nn::Priority::DEFAULT, {}, {}, {}, {});
|
||||
notify(callback.get(), std::move(result), executor, userId);
|
||||
};
|
||||
executor(std::move(task), userId, {});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
nn::GeneralResult<void> prepareModel_1_2(const nn::SharedDevice& device, const Executor& executor,
|
||||
const V1_2::Model& model,
|
||||
V1_1::ExecutionPreference preference,
|
||||
const hidl_vec<hidl_handle>& modelCache,
|
||||
const hidl_vec<hidl_handle>& dataCache,
|
||||
const CacheToken& token,
|
||||
const sp<V1_2::IPreparedModelCallback>& callback) {
|
||||
if (callback.get() == nullptr) {
|
||||
return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback";
|
||||
}
|
||||
|
||||
auto nnModel = NN_TRY(convertInput(model));
|
||||
const auto nnPreference = NN_TRY(convertInput(preference));
|
||||
auto nnModelCache = NN_TRY(convertInput(modelCache));
|
||||
auto nnDataCache = NN_TRY(convertInput(dataCache));
|
||||
const auto nnToken = nn::CacheToken(token);
|
||||
|
||||
const uid_t userId = hardware::IPCThreadState::self()->getCallingUid();
|
||||
Task task = [device, nnModel = std::move(nnModel), nnPreference,
|
||||
nnModelCache = std::move(nnModelCache), nnDataCache = std::move(nnDataCache),
|
||||
nnToken, userId, executor, callback] {
|
||||
auto result = device->prepareModel(nnModel, nnPreference, nn::Priority::DEFAULT, {},
|
||||
nnModelCache, nnDataCache, nnToken);
|
||||
notify(callback.get(), std::move(result), executor, userId);
|
||||
};
|
||||
executor(std::move(task), userId, {});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
nn::GeneralResult<void> prepareModel_1_3(
|
||||
const nn::SharedDevice& device, const Executor& executor, const V1_3::Model& model,
|
||||
V1_1::ExecutionPreference preference, V1_3::Priority priority,
|
||||
const V1_3::OptionalTimePoint& deadline, const hidl_vec<hidl_handle>& modelCache,
|
||||
const hidl_vec<hidl_handle>& dataCache, const CacheToken& token,
|
||||
const sp<V1_3::IPreparedModelCallback>& callback) {
|
||||
if (callback.get() == nullptr) {
|
||||
return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback";
|
||||
}
|
||||
|
||||
auto nnModel = NN_TRY(convertInput(model));
|
||||
const auto nnPreference = NN_TRY(convertInput(preference));
|
||||
const auto nnPriority = NN_TRY(convertInput(priority));
|
||||
const auto nnDeadline = NN_TRY(convertInput(deadline));
|
||||
auto nnModelCache = NN_TRY(convertInput(modelCache));
|
||||
auto nnDataCache = NN_TRY(convertInput(dataCache));
|
||||
const auto nnToken = nn::CacheToken(token);
|
||||
|
||||
const uid_t userId = hardware::IPCThreadState::self()->getCallingUid();
|
||||
Task task = [device, nnModel = std::move(nnModel), nnPreference, nnPriority, nnDeadline,
|
||||
nnModelCache = std::move(nnModelCache), nnDataCache = std::move(nnDataCache),
|
||||
nnToken, userId, executor, callback] {
|
||||
auto result = device->prepareModel(nnModel, nnPreference, nnPriority, nnDeadline,
|
||||
nnModelCache, nnDataCache, nnToken);
|
||||
notify(callback.get(), std::move(result), executor, userId);
|
||||
};
|
||||
executor(std::move(task), userId, nnDeadline);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
nn::GeneralResult<void> prepareModelFromCache(const nn::SharedDevice& device,
|
||||
const Executor& executor,
|
||||
const hidl_vec<hidl_handle>& modelCache,
|
||||
const hidl_vec<hidl_handle>& dataCache,
|
||||
const CacheToken& token,
|
||||
const sp<V1_2::IPreparedModelCallback>& callback) {
|
||||
if (callback.get() == nullptr) {
|
||||
return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback";
|
||||
}
|
||||
|
||||
auto nnModelCache = NN_TRY(convertInput(modelCache));
|
||||
auto nnDataCache = NN_TRY(convertInput(dataCache));
|
||||
const auto nnToken = nn::CacheToken(token);
|
||||
|
||||
const uid_t userId = hardware::IPCThreadState::self()->getCallingUid();
|
||||
Task task = [device, nnModelCache = std::move(nnModelCache),
|
||||
nnDataCache = std::move(nnDataCache), nnToken, userId, executor, callback] {
|
||||
auto result = device->prepareModelFromCache({}, nnModelCache, nnDataCache, nnToken);
|
||||
notify(callback.get(), std::move(result), executor, userId);
|
||||
};
|
||||
executor(std::move(task), userId, {});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
nn::GeneralResult<void> prepareModelFromCache_1_3(
|
||||
const nn::SharedDevice& device, const Executor& executor,
|
||||
const V1_3::OptionalTimePoint& deadline, const hidl_vec<hidl_handle>& modelCache,
|
||||
const hidl_vec<hidl_handle>& dataCache, const CacheToken& token,
|
||||
const sp<V1_3::IPreparedModelCallback>& callback) {
|
||||
if (callback.get() == nullptr) {
|
||||
return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback";
|
||||
}
|
||||
|
||||
const auto nnDeadline = NN_TRY(convertInput(deadline));
|
||||
auto nnModelCache = NN_TRY(convertInput(modelCache));
|
||||
auto nnDataCache = NN_TRY(convertInput(dataCache));
|
||||
const auto nnToken = nn::CacheToken(token);
|
||||
|
||||
const uid_t userId = hardware::IPCThreadState::self()->getCallingUid();
|
||||
auto task = [device, nnDeadline, nnModelCache = std::move(nnModelCache),
|
||||
nnDataCache = std::move(nnDataCache), nnToken, userId, executor, callback] {
|
||||
auto result = device->prepareModelFromCache(nnDeadline, nnModelCache, nnDataCache, nnToken);
|
||||
notify(callback.get(), std::move(result), executor, userId);
|
||||
};
|
||||
executor(std::move(task), userId, nnDeadline);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
nn::GeneralResult<nn::SharedPreparedModel> downcast(const sp<V1_3::IPreparedModel>& preparedModel) {
|
||||
if (preparedModel == nullptr) {
|
||||
return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "preparedModel is nullptr";
|
||||
}
|
||||
if (preparedModel->isRemote()) {
|
||||
return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Cannot convert remote models";
|
||||
}
|
||||
|
||||
// This static_cast is safe because adapter::PreparedModel is the only class that implements
|
||||
// the IPreparedModel interface in the adapter service code.
|
||||
const auto* casted = static_cast<const PreparedModel*>(preparedModel.get());
|
||||
return casted->getUnderlyingPreparedModel();
|
||||
}
|
||||
|
||||
nn::GeneralResult<std::vector<nn::SharedPreparedModel>> downcastAll(
|
||||
const hidl_vec<sp<V1_3::IPreparedModel>>& preparedModels) {
|
||||
std::vector<nn::SharedPreparedModel> canonical;
|
||||
canonical.reserve(preparedModels.size());
|
||||
for (const auto& preparedModel : preparedModels) {
|
||||
canonical.push_back(NN_TRY(downcast(preparedModel)));
|
||||
}
|
||||
return canonical;
|
||||
}
|
||||
|
||||
nn::GeneralResult<std::pair<sp<V1_3::IBuffer>, uint32_t>> allocate(
|
||||
const nn::SharedDevice& device, const V1_3::BufferDesc& desc,
|
||||
const hidl_vec<sp<V1_3::IPreparedModel>>& preparedModels,
|
||||
const hidl_vec<V1_3::BufferRole>& inputRoles,
|
||||
const hidl_vec<V1_3::BufferRole>& outputRoles) {
|
||||
auto nnDesc = NN_TRY(convertInput(desc));
|
||||
auto nnPreparedModels = NN_TRY(downcastAll(preparedModels));
|
||||
auto nnInputRoles = NN_TRY(convertInput(inputRoles));
|
||||
auto nnOutputRoles = NN_TRY(convertInput(outputRoles));
|
||||
|
||||
auto buffer = NN_TRY(device->allocate(nnDesc, nnPreparedModels, nnInputRoles, nnOutputRoles));
|
||||
|
||||
const nn::Request::MemoryDomainToken token = buffer->getToken();
|
||||
auto hidlBuffer = sp<Buffer>::make(std::move(buffer));
|
||||
return std::make_pair(std::move(hidlBuffer), static_cast<uint32_t>(token));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Device::Device(nn::SharedDevice device, Executor executor)
|
||||
: kDevice(std::move(device)), kExecutor(std::move(executor)) {
|
||||
CHECK(kDevice != nullptr);
|
||||
CHECK(kExecutor != nullptr);
|
||||
}
|
||||
|
||||
Return<void> Device::getCapabilities(getCapabilities_cb cb) {
|
||||
const auto capabilities = V1_0::utils::convert(kDevice->getCapabilities()).value();
|
||||
cb(V1_0::ErrorStatus::NONE, capabilities);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Device::getCapabilities_1_1(getCapabilities_1_1_cb cb) {
|
||||
const auto capabilities = V1_1::utils::convert(kDevice->getCapabilities()).value();
|
||||
cb(V1_0::ErrorStatus::NONE, capabilities);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Device::getCapabilities_1_2(getCapabilities_1_2_cb cb) {
|
||||
const auto capabilities = V1_2::utils::convert(kDevice->getCapabilities()).value();
|
||||
cb(V1_0::ErrorStatus::NONE, capabilities);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Device::getCapabilities_1_3(getCapabilities_1_3_cb cb) {
|
||||
const auto capabilities = V1_3::utils::convert(kDevice->getCapabilities()).value();
|
||||
cb(V1_3::ErrorStatus::NONE, capabilities);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Device::getVersionString(getVersionString_cb cb) {
|
||||
cb(V1_0::ErrorStatus::NONE, kDevice->getVersionString());
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Device::getType(getType_cb cb) {
|
||||
const auto maybeDeviceType = V1_2::utils::convert(kDevice->getType());
|
||||
if (!maybeDeviceType.has_value()) {
|
||||
const auto& [message, code] = maybeDeviceType.error();
|
||||
LOG(ERROR) << "adapter::Device::getType failed with " << code << ": " << message;
|
||||
cb(V1_2::utils::convert(code).value(), {});
|
||||
} else {
|
||||
cb(V1_0::ErrorStatus::NONE, maybeDeviceType.value());
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Device::getSupportedExtensions(getSupportedExtensions_cb cb) {
|
||||
const auto maybeSupportedExtensions = V1_2::utils::convert(kDevice->getSupportedExtensions());
|
||||
if (!maybeSupportedExtensions.has_value()) {
|
||||
const auto& [message, code] = maybeSupportedExtensions.error();
|
||||
LOG(ERROR) << "adapter::Device::getSupportedExtensions failed with " << code << ": "
|
||||
<< message;
|
||||
cb(V1_2::utils::convert(code).value(), {});
|
||||
} else {
|
||||
cb(V1_0::ErrorStatus::NONE, maybeSupportedExtensions.value());
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Device::getSupportedOperations(const V1_0::Model& model,
|
||||
getSupportedOperations_cb cb) {
|
||||
const auto result = adapter::getSupportedOperations(kDevice, model);
|
||||
if (!result.has_value()) {
|
||||
const auto& [message, code] = result.error();
|
||||
LOG(ERROR) << "adapter::Device::getSupportedOperations_1_0 failed with " << code << ": "
|
||||
<< message;
|
||||
cb(V1_0::utils::convert(code).value(), {});
|
||||
} else {
|
||||
cb(V1_0::ErrorStatus::NONE, result.value());
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Device::getSupportedOperations_1_1(const V1_1::Model& model,
|
||||
getSupportedOperations_1_1_cb cb) {
|
||||
const auto result = adapter::getSupportedOperations(kDevice, model);
|
||||
if (!result.has_value()) {
|
||||
const auto& [message, code] = result.error();
|
||||
LOG(ERROR) << "adapter::Device::getSupportedOperations_1_1 failed with " << code << ": "
|
||||
<< message;
|
||||
cb(V1_1::utils::convert(code).value(), {});
|
||||
} else {
|
||||
cb(V1_0::ErrorStatus::NONE, result.value());
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Device::getSupportedOperations_1_2(const V1_2::Model& model,
|
||||
getSupportedOperations_1_2_cb cb) {
|
||||
const auto result = adapter::getSupportedOperations(kDevice, model);
|
||||
if (!result.has_value()) {
|
||||
const auto& [message, code] = result.error();
|
||||
LOG(ERROR) << "adapter::Device::getSupportedOperations_1_2 failed with " << code << ": "
|
||||
<< message;
|
||||
cb(V1_2::utils::convert(code).value(), {});
|
||||
} else {
|
||||
cb(V1_0::ErrorStatus::NONE, result.value());
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Device::getSupportedOperations_1_3(const V1_3::Model& model,
|
||||
getSupportedOperations_1_3_cb cb) {
|
||||
const auto result = adapter::getSupportedOperations(kDevice, model);
|
||||
if (!result.has_value()) {
|
||||
const auto& [message, code] = result.error();
|
||||
LOG(ERROR) << "adapter::Device::getSupportedOperations_1_3 failed with " << code << ": "
|
||||
<< message;
|
||||
cb(V1_3::utils::convert(code).value(), {});
|
||||
} else {
|
||||
cb(V1_3::ErrorStatus::NONE, result.value());
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Device::getNumberOfCacheFilesNeeded(getNumberOfCacheFilesNeeded_cb cb) {
|
||||
const auto [numModelCache, numDataCache] = kDevice->getNumberOfCacheFilesNeeded();
|
||||
cb(V1_0::ErrorStatus::NONE, numModelCache, numDataCache);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<V1_0::ErrorStatus> Device::prepareModel(const V1_0::Model& model,
|
||||
const sp<V1_0::IPreparedModelCallback>& callback) {
|
||||
auto result = adapter::prepareModel(kDevice, kExecutor, model, callback);
|
||||
if (!result.has_value()) {
|
||||
auto [message, code] = std::move(result).error();
|
||||
LOG(ERROR) << "adapter::Device::prepareModel failed with " << code << ": " << message;
|
||||
notify(callback.get(), code, nullptr);
|
||||
return V1_0::utils::convert(code).value();
|
||||
}
|
||||
return V1_0::ErrorStatus::NONE;
|
||||
}
|
||||
|
||||
Return<V1_0::ErrorStatus> Device::prepareModel_1_1(
|
||||
const V1_1::Model& model, V1_1::ExecutionPreference preference,
|
||||
const sp<V1_0::IPreparedModelCallback>& callback) {
|
||||
auto result = adapter::prepareModel_1_1(kDevice, kExecutor, model, preference, callback);
|
||||
if (!result.has_value()) {
|
||||
auto [message, code] = std::move(result).error();
|
||||
LOG(ERROR) << "adapter::Device::prepareModel_1_1 failed with " << code << ": " << message;
|
||||
notify(callback.get(), code, nullptr);
|
||||
return V1_1::utils::convert(code).value();
|
||||
}
|
||||
return V1_0::ErrorStatus::NONE;
|
||||
}
|
||||
|
||||
Return<V1_0::ErrorStatus> Device::prepareModel_1_2(
|
||||
const V1_2::Model& model, V1_1::ExecutionPreference preference,
|
||||
const hidl_vec<hidl_handle>& modelCache, const hidl_vec<hidl_handle>& dataCache,
|
||||
const CacheToken& token, const sp<V1_2::IPreparedModelCallback>& callback) {
|
||||
auto result = adapter::prepareModel_1_2(kDevice, kExecutor, model, preference, modelCache,
|
||||
dataCache, token, callback);
|
||||
if (!result.has_value()) {
|
||||
auto [message, code] = std::move(result).error();
|
||||
LOG(ERROR) << "adapter::Device::prepareModel_1_2 failed with " << code << ": " << message;
|
||||
notify(callback.get(), code, nullptr);
|
||||
return V1_2::utils::convert(code).value();
|
||||
}
|
||||
return V1_0::ErrorStatus::NONE;
|
||||
}
|
||||
|
||||
Return<V1_3::ErrorStatus> Device::prepareModel_1_3(
|
||||
const V1_3::Model& model, V1_1::ExecutionPreference preference, V1_3::Priority priority,
|
||||
const V1_3::OptionalTimePoint& deadline, const hidl_vec<hidl_handle>& modelCache,
|
||||
const hidl_vec<hidl_handle>& dataCache, const CacheToken& token,
|
||||
const sp<V1_3::IPreparedModelCallback>& callback) {
|
||||
auto result = adapter::prepareModel_1_3(kDevice, kExecutor, model, preference, priority,
|
||||
deadline, modelCache, dataCache, token, callback);
|
||||
if (!result.has_value()) {
|
||||
auto [message, code] = std::move(result).error();
|
||||
LOG(ERROR) << "adapter::Device::prepareModel_1_3 failed with " << code << ": " << message;
|
||||
notify(callback.get(), code, nullptr);
|
||||
return V1_3::utils::convert(code).value();
|
||||
}
|
||||
return V1_3::ErrorStatus::NONE;
|
||||
}
|
||||
|
||||
Return<V1_0::ErrorStatus> Device::prepareModelFromCache(
|
||||
const hidl_vec<hidl_handle>& modelCache, const hidl_vec<hidl_handle>& dataCache,
|
||||
const CacheToken& token, const sp<V1_2::IPreparedModelCallback>& callback) {
|
||||
auto result = adapter::prepareModelFromCache(kDevice, kExecutor, modelCache, dataCache, token,
|
||||
callback);
|
||||
if (!result.has_value()) {
|
||||
auto [message, code] = std::move(result).error();
|
||||
LOG(ERROR) << "adapter::Device::prepareModelFromCache failed with " << code << ": "
|
||||
<< message;
|
||||
notify(callback.get(), code, nullptr);
|
||||
return V1_2::utils::convert(code).value();
|
||||
}
|
||||
return V1_0::ErrorStatus::NONE;
|
||||
}
|
||||
|
||||
Return<V1_3::ErrorStatus> Device::prepareModelFromCache_1_3(
|
||||
const V1_3::OptionalTimePoint& deadline, const hidl_vec<hidl_handle>& modelCache,
|
||||
const hidl_vec<hidl_handle>& dataCache, const CacheToken& token,
|
||||
const sp<V1_3::IPreparedModelCallback>& callback) {
|
||||
auto result = adapter::prepareModelFromCache_1_3(kDevice, kExecutor, deadline, modelCache,
|
||||
dataCache, token, callback);
|
||||
if (!result.has_value()) {
|
||||
auto [message, code] = std::move(result).error();
|
||||
LOG(ERROR) << "adapter::Device::prepareModelFromCache_1_3 failed with " << code << ": "
|
||||
<< message;
|
||||
notify(callback.get(), code, nullptr);
|
||||
return V1_3::utils::convert(code).value();
|
||||
}
|
||||
return V1_3::ErrorStatus::NONE;
|
||||
}
|
||||
|
||||
Return<V1_0::DeviceStatus> Device::getStatus() {
|
||||
return V1_0::DeviceStatus::AVAILABLE;
|
||||
}
|
||||
|
||||
Return<void> Device::allocate(const V1_3::BufferDesc& desc,
|
||||
const hidl_vec<sp<V1_3::IPreparedModel>>& preparedModels,
|
||||
const hidl_vec<V1_3::BufferRole>& inputRoles,
|
||||
const hidl_vec<V1_3::BufferRole>& outputRoles, allocate_cb cb) {
|
||||
auto result = adapter::allocate(kDevice, desc, preparedModels, inputRoles, outputRoles);
|
||||
if (!result.has_value()) {
|
||||
const auto [message, code] = std::move(result).error();
|
||||
LOG(ERROR) << "adapter::Device::allocate failed with " << code << ": " << message;
|
||||
cb(V1_3::utils::convert(code).value(), nullptr, /*token=*/0);
|
||||
return Void();
|
||||
}
|
||||
auto [buffer, token] = std::move(result).value();
|
||||
cb(V1_3::ErrorStatus::NONE, buffer, token);
|
||||
return Void();
|
||||
}
|
||||
|
||||
} // namespace android::hardware::neuralnetworks::adapter
|
417
neuralnetworks/utils/adapter/src/PreparedModel.cpp
Normal file
417
neuralnetworks/utils/adapter/src/PreparedModel.cpp
Normal file
|
@ -0,0 +1,417 @@
|
|||
/*
|
||||
* 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 "PreparedModel.h"
|
||||
|
||||
#include <ExecutionBurstServer.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
|
||||
#include <android/hardware/neuralnetworks/1.0/types.h>
|
||||
#include <android/hardware/neuralnetworks/1.2/IBurstCallback.h>
|
||||
#include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
|
||||
#include <android/hardware/neuralnetworks/1.2/types.h>
|
||||
#include <android/hardware/neuralnetworks/1.3/IExecutionCallback.h>
|
||||
#include <android/hardware/neuralnetworks/1.3/IFencedExecutionCallback.h>
|
||||
#include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
|
||||
#include <android/hardware/neuralnetworks/1.3/types.h>
|
||||
#include <hwbinder/IPCThreadState.h>
|
||||
#include <nnapi/IPreparedModel.h>
|
||||
#include <nnapi/TypeUtils.h>
|
||||
#include <nnapi/Types.h>
|
||||
#include <nnapi/Validation.h>
|
||||
#include <nnapi/hal/1.0/Utils.h>
|
||||
#include <nnapi/hal/1.2/Utils.h>
|
||||
#include <nnapi/hal/1.3/Conversions.h>
|
||||
#include <nnapi/hal/1.3/Utils.h>
|
||||
#include <nnapi/hal/HandleError.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
|
||||
// lifetimes across processes and for protecting asynchronous calls across HIDL.
|
||||
|
||||
namespace android::hardware::neuralnetworks::adapter {
|
||||
namespace {
|
||||
|
||||
template <typename Type>
|
||||
auto convertInput(const Type& object) -> decltype(nn::convert(std::declval<Type>())) {
|
||||
auto result = nn::convert(object);
|
||||
if (!result.has_value()) {
|
||||
result.error().code = nn::ErrorStatus::INVALID_ARGUMENT;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
class FencedExecutionCallback final : public V1_3::IFencedExecutionCallback {
|
||||
public:
|
||||
explicit FencedExecutionCallback(const nn::ExecuteFencedInfoCallback& callback)
|
||||
: kCallback(callback) {
|
||||
CHECK(callback != nullptr);
|
||||
}
|
||||
|
||||
Return<void> getExecutionInfo(getExecutionInfo_cb cb) override {
|
||||
const auto result = kCallback();
|
||||
if (!result.has_value()) {
|
||||
const auto& [message, code] = result.error();
|
||||
const auto status =
|
||||
V1_3::utils::convert(code).value_or(V1_3::ErrorStatus::GENERAL_FAILURE);
|
||||
LOG(ERROR) << message;
|
||||
cb(status, V1_2::utils::kNoTiming, V1_2::utils::kNoTiming);
|
||||
return Void();
|
||||
}
|
||||
const auto [timingLaunched, timingFenced] = result.value();
|
||||
const auto hidlTimingLaunched = V1_3::utils::convert(timingLaunched).value();
|
||||
const auto hidlTimingFenced = V1_3::utils::convert(timingFenced).value();
|
||||
cb(V1_3::ErrorStatus::NONE, hidlTimingLaunched, hidlTimingFenced);
|
||||
return Void();
|
||||
}
|
||||
|
||||
private:
|
||||
const nn::ExecuteFencedInfoCallback kCallback;
|
||||
};
|
||||
|
||||
using ExecutionResult = nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>;
|
||||
|
||||
void notify(V1_0::IExecutionCallback* callback, nn::ErrorStatus status,
|
||||
const std::vector<nn::OutputShape>& /*outputShapes*/, const nn::Timing& /*timing*/) {
|
||||
if (callback != nullptr) {
|
||||
const auto hidlStatus = V1_0::utils::convert(status).value();
|
||||
const auto ret = callback->notify(hidlStatus);
|
||||
if (!ret.isOk()) {
|
||||
LOG(ERROR) << "V1_0::IExecutionCallback::notify failed with " << ret.description();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void notify(V1_2::IExecutionCallback* callback, nn::ErrorStatus status,
|
||||
const std::vector<nn::OutputShape>& outputShapes, const nn::Timing& timing) {
|
||||
if (callback != nullptr) {
|
||||
const auto hidlStatus = V1_2::utils::convert(status).value();
|
||||
const auto hidlOutputShapes = V1_2::utils::convert(outputShapes).value();
|
||||
const auto hidlTiming = V1_2::utils::convert(timing).value();
|
||||
const auto ret = callback->notify_1_2(hidlStatus, hidlOutputShapes, hidlTiming);
|
||||
if (!ret.isOk()) {
|
||||
LOG(ERROR) << "V1_2::IExecutionCallback::notify_1_2 failed with " << ret.description();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void notify(V1_3::IExecutionCallback* callback, nn::ErrorStatus status,
|
||||
const std::vector<nn::OutputShape>& outputShapes, const nn::Timing& timing) {
|
||||
if (callback != nullptr) {
|
||||
const auto hidlStatus = V1_3::utils::convert(status).value();
|
||||
const auto hidlOutputShapes = V1_3::utils::convert(outputShapes).value();
|
||||
const auto hidlTiming = V1_3::utils::convert(timing).value();
|
||||
const auto ret = callback->notify_1_3(hidlStatus, hidlOutputShapes, hidlTiming);
|
||||
if (!ret.isOk()) {
|
||||
LOG(ERROR) << "V1_3::IExecutionCallback::notify_1_3 failed with " << ret.description();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename CallbackType>
|
||||
void notify(CallbackType* callback, ExecutionResult result) {
|
||||
if (!result.has_value()) {
|
||||
const auto [message, status, outputShapes] = std::move(result).error();
|
||||
LOG(ERROR) << message;
|
||||
notify(callback, status, outputShapes, {});
|
||||
} else {
|
||||
const auto [outputShapes, timing] = std::move(result).value();
|
||||
notify(callback, nn::ErrorStatus::NONE, outputShapes, timing);
|
||||
}
|
||||
}
|
||||
|
||||
nn::GeneralResult<void> execute(const nn::SharedPreparedModel& preparedModel, uid_t userId,
|
||||
const Executor& executor, const V1_0::Request& request,
|
||||
const sp<V1_0::IExecutionCallback>& callback) {
|
||||
if (callback.get() == nullptr) {
|
||||
return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback";
|
||||
}
|
||||
|
||||
auto nnRequest = NN_TRY(convertInput(request));
|
||||
|
||||
const std::any resource = preparedModel->getUnderlyingResource();
|
||||
if (const auto* model = std::any_cast<const nn::Model*>(&resource)) {
|
||||
CHECK(*model != nullptr);
|
||||
NN_TRY(utils::makeGeneralFailure(nn::validateRequestForModel(nnRequest, **model),
|
||||
nn::ErrorStatus::INVALID_ARGUMENT));
|
||||
}
|
||||
|
||||
Task task = [preparedModel, nnRequest = std::move(nnRequest), callback] {
|
||||
auto result = preparedModel->execute(nnRequest, nn::MeasureTiming::NO, {}, {});
|
||||
notify(callback.get(), std::move(result));
|
||||
};
|
||||
executor(std::move(task), userId, {});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
nn::GeneralResult<void> execute_1_2(const nn::SharedPreparedModel& preparedModel, uid_t userId,
|
||||
const Executor& executor, const V1_0::Request& request,
|
||||
V1_2::MeasureTiming measure,
|
||||
const sp<V1_2::IExecutionCallback>& callback) {
|
||||
if (callback.get() == nullptr) {
|
||||
return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback";
|
||||
}
|
||||
|
||||
auto nnRequest = NN_TRY(convertInput(request));
|
||||
const auto nnMeasure = NN_TRY(convertInput(measure));
|
||||
|
||||
const std::any resource = preparedModel->getUnderlyingResource();
|
||||
if (const auto* model = std::any_cast<const nn::Model*>(&resource)) {
|
||||
CHECK(*model != nullptr);
|
||||
NN_TRY(utils::makeGeneralFailure(nn::validateRequestForModel(nnRequest, **model),
|
||||
nn::ErrorStatus::INVALID_ARGUMENT));
|
||||
}
|
||||
|
||||
Task task = [preparedModel, nnRequest = std::move(nnRequest), nnMeasure, callback] {
|
||||
auto result = preparedModel->execute(nnRequest, nnMeasure, {}, {});
|
||||
notify(callback.get(), std::move(result));
|
||||
};
|
||||
executor(std::move(task), userId, {});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
nn::GeneralResult<void> execute_1_3(const nn::SharedPreparedModel& preparedModel, uid_t userId,
|
||||
const Executor& executor, const V1_3::Request& request,
|
||||
V1_2::MeasureTiming measure,
|
||||
const V1_3::OptionalTimePoint& deadline,
|
||||
const V1_3::OptionalTimeoutDuration& loopTimeoutDuration,
|
||||
const sp<V1_3::IExecutionCallback>& callback) {
|
||||
if (callback.get() == nullptr) {
|
||||
return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback";
|
||||
}
|
||||
|
||||
auto nnRequest = NN_TRY(convertInput(request));
|
||||
const auto nnMeasure = NN_TRY(convertInput(measure));
|
||||
const auto nnDeadline = NN_TRY(convertInput(deadline));
|
||||
const auto nnLoopTimeoutDuration = NN_TRY(convertInput(loopTimeoutDuration));
|
||||
|
||||
const std::any resource = preparedModel->getUnderlyingResource();
|
||||
if (const auto* model = std::any_cast<const nn::Model*>(&resource)) {
|
||||
CHECK(*model != nullptr);
|
||||
NN_TRY(utils::makeGeneralFailure(nn::validateRequestForModel(nnRequest, **model),
|
||||
nn::ErrorStatus::INVALID_ARGUMENT));
|
||||
}
|
||||
|
||||
Task task = [preparedModel, nnRequest = std::move(nnRequest), nnMeasure, nnDeadline,
|
||||
nnLoopTimeoutDuration, callback] {
|
||||
auto result =
|
||||
preparedModel->execute(nnRequest, nnMeasure, nnDeadline, nnLoopTimeoutDuration);
|
||||
notify(callback.get(), std::move(result));
|
||||
};
|
||||
executor(std::move(task), userId, nnDeadline);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
nn::ExecutionResult<std::pair<hidl_vec<V1_2::OutputShape>, V1_2::Timing>> executeSynchronously(
|
||||
const nn::SharedPreparedModel& preparedModel, const V1_0::Request& request,
|
||||
V1_2::MeasureTiming measure) {
|
||||
const auto nnRequest = NN_TRY(utils::makeExecutionFailure(convertInput(request)));
|
||||
const auto nnMeasure = NN_TRY(utils::makeExecutionFailure(convertInput(measure)));
|
||||
|
||||
const auto [outputShapes, timing] =
|
||||
NN_TRY(preparedModel->execute(nnRequest, nnMeasure, {}, {}));
|
||||
|
||||
auto hidlOutputShapes = NN_TRY(utils::makeExecutionFailure(V1_2::utils::convert(outputShapes)));
|
||||
const auto hidlTiming = NN_TRY(utils::makeExecutionFailure(V1_2::utils::convert(timing)));
|
||||
return std::make_pair(std::move(hidlOutputShapes), hidlTiming);
|
||||
}
|
||||
|
||||
nn::ExecutionResult<std::pair<hidl_vec<V1_2::OutputShape>, V1_2::Timing>> executeSynchronously_1_3(
|
||||
const nn::SharedPreparedModel& preparedModel, const V1_3::Request& request,
|
||||
V1_2::MeasureTiming measure, const V1_3::OptionalTimePoint& deadline,
|
||||
const V1_3::OptionalTimeoutDuration& loopTimeoutDuration) {
|
||||
const auto nnRequest = NN_TRY(utils::makeExecutionFailure(convertInput(request)));
|
||||
const auto nnMeasure = NN_TRY(utils::makeExecutionFailure(convertInput(measure)));
|
||||
const auto nnDeadline = NN_TRY(utils::makeExecutionFailure(convertInput(deadline)));
|
||||
const auto nnLoopTimeoutDuration =
|
||||
NN_TRY(utils::makeExecutionFailure(convertInput(loopTimeoutDuration)));
|
||||
|
||||
const auto [outputShapes, timing] =
|
||||
NN_TRY(preparedModel->execute(nnRequest, nnMeasure, nnDeadline, nnLoopTimeoutDuration));
|
||||
|
||||
auto hidlOutputShapes = NN_TRY(utils::makeExecutionFailure(V1_3::utils::convert(outputShapes)));
|
||||
const auto hidlTiming = NN_TRY(utils::makeExecutionFailure(V1_3::utils::convert(timing)));
|
||||
return std::make_pair(std::move(hidlOutputShapes), hidlTiming);
|
||||
}
|
||||
|
||||
nn::GeneralResult<std::vector<nn::SyncFence>> convertSyncFences(
|
||||
const hidl_vec<hidl_handle>& handles) {
|
||||
std::vector<nn::SyncFence> syncFences;
|
||||
syncFences.reserve(handles.size());
|
||||
for (const auto& handle : handles) {
|
||||
auto nativeHandle = NN_TRY(convertInput(handle));
|
||||
auto syncFence = NN_TRY(utils::makeGeneralFailure(
|
||||
nn::SyncFence::create(std::move(nativeHandle)), nn::ErrorStatus::INVALID_ARGUMENT));
|
||||
syncFences.push_back(std::move(syncFence));
|
||||
}
|
||||
return syncFences;
|
||||
}
|
||||
|
||||
nn::GeneralResult<std::pair<hidl_handle, sp<V1_3::IFencedExecutionCallback>>> executeFenced(
|
||||
const nn::SharedPreparedModel& preparedModel, const V1_3::Request& request,
|
||||
const hidl_vec<hidl_handle>& waitFor, V1_2::MeasureTiming measure,
|
||||
const V1_3::OptionalTimePoint& deadline,
|
||||
const V1_3::OptionalTimeoutDuration& loopTimeoutDuration,
|
||||
const V1_3::OptionalTimeoutDuration& duration) {
|
||||
const auto nnRequest = NN_TRY(convertInput(request));
|
||||
const auto nnWaitFor = NN_TRY(convertSyncFences(waitFor));
|
||||
const auto nnMeasure = NN_TRY(convertInput(measure));
|
||||
const auto nnDeadline = NN_TRY(convertInput(deadline));
|
||||
const auto nnLoopTimeoutDuration = NN_TRY(convertInput(loopTimeoutDuration));
|
||||
const auto nnDuration = NN_TRY(convertInput(duration));
|
||||
|
||||
auto [syncFence, executeFencedCallback] = NN_TRY(preparedModel->executeFenced(
|
||||
nnRequest, nnWaitFor, nnMeasure, nnDeadline, nnLoopTimeoutDuration, nnDuration));
|
||||
|
||||
auto hidlSyncFence = NN_TRY(V1_3::utils::convert(syncFence.getSharedHandle()));
|
||||
auto hidlExecuteFencedCallback = sp<FencedExecutionCallback>::make(executeFencedCallback);
|
||||
return std::make_pair(std::move(hidlSyncFence), std::move(hidlExecuteFencedCallback));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PreparedModel::PreparedModel(nn::SharedPreparedModel preparedModel, Executor executor, uid_t userId)
|
||||
: kPreparedModel(std::move(preparedModel)), kExecutor(std::move(executor)), kUserId(userId) {
|
||||
CHECK(kPreparedModel != nullptr);
|
||||
CHECK(kExecutor != nullptr);
|
||||
}
|
||||
|
||||
nn::SharedPreparedModel PreparedModel::getUnderlyingPreparedModel() const {
|
||||
return kPreparedModel;
|
||||
}
|
||||
|
||||
Return<V1_0::ErrorStatus> PreparedModel::execute(const V1_0::Request& request,
|
||||
const sp<V1_0::IExecutionCallback>& callback) {
|
||||
auto result = adapter::execute(kPreparedModel, kUserId, kExecutor, request, callback);
|
||||
if (!result.has_value()) {
|
||||
auto [message, code] = std::move(result).error();
|
||||
LOG(ERROR) << "adapter::PreparedModel::execute failed with " << code << ": " << message;
|
||||
notify(callback.get(), code, {}, {});
|
||||
return V1_0::utils::convert(code).value();
|
||||
}
|
||||
return V1_0::ErrorStatus::NONE;
|
||||
}
|
||||
|
||||
Return<V1_0::ErrorStatus> PreparedModel::execute_1_2(const V1_0::Request& request,
|
||||
V1_2::MeasureTiming measure,
|
||||
const sp<V1_2::IExecutionCallback>& callback) {
|
||||
auto result =
|
||||
adapter::execute_1_2(kPreparedModel, kUserId, kExecutor, request, measure, callback);
|
||||
if (!result.has_value()) {
|
||||
auto [message, code] = std::move(result).error();
|
||||
LOG(ERROR) << "adapter::PreparedModel::execute_1_2 failed with " << code << ": " << message;
|
||||
notify(callback.get(), code, {}, {});
|
||||
return V1_2::utils::convert(code).value();
|
||||
}
|
||||
return V1_0::ErrorStatus::NONE;
|
||||
}
|
||||
|
||||
Return<V1_3::ErrorStatus> PreparedModel::execute_1_3(
|
||||
const V1_3::Request& request, V1_2::MeasureTiming measure,
|
||||
const V1_3::OptionalTimePoint& deadline,
|
||||
const V1_3::OptionalTimeoutDuration& loopTimeoutDuration,
|
||||
const sp<V1_3::IExecutionCallback>& callback) {
|
||||
auto result = adapter::execute_1_3(kPreparedModel, kUserId, kExecutor, request, measure,
|
||||
deadline, loopTimeoutDuration, callback);
|
||||
if (!result.has_value()) {
|
||||
auto [message, code] = std::move(result).error();
|
||||
LOG(ERROR) << "adapter::PreparedModel::execute_1_3 failed with " << code << ": " << message;
|
||||
notify(callback.get(), code, {}, {});
|
||||
return V1_3::utils::convert(code).value();
|
||||
}
|
||||
return V1_3::ErrorStatus::NONE;
|
||||
}
|
||||
|
||||
Return<void> PreparedModel::executeSynchronously(const V1_0::Request& request,
|
||||
V1_2::MeasureTiming measure,
|
||||
executeSynchronously_cb cb) {
|
||||
auto result = adapter::executeSynchronously(kPreparedModel, request, measure);
|
||||
if (!result.has_value()) {
|
||||
auto [message, code, outputShapes] = std::move(result).error();
|
||||
LOG(ERROR) << "adapter::PreparedModel::executeSynchronously failed with " << code << ": "
|
||||
<< message;
|
||||
cb(V1_2::utils::convert(code).value(), V1_2::utils::convert(outputShapes).value(),
|
||||
V1_2::utils::kNoTiming);
|
||||
return Void();
|
||||
}
|
||||
auto [outputShapes, timing] = std::move(result).value();
|
||||
cb(V1_0::ErrorStatus::NONE, outputShapes, timing);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> PreparedModel::executeSynchronously_1_3(
|
||||
const V1_3::Request& request, V1_2::MeasureTiming measure,
|
||||
const V1_3::OptionalTimePoint& deadline,
|
||||
const V1_3::OptionalTimeoutDuration& loopTimeoutDuration, executeSynchronously_1_3_cb cb) {
|
||||
auto result = adapter::executeSynchronously_1_3(kPreparedModel, request, measure, deadline,
|
||||
loopTimeoutDuration);
|
||||
if (!result.has_value()) {
|
||||
auto [message, code, outputShapes] = std::move(result).error();
|
||||
LOG(ERROR) << "adapter::PreparedModel::executeSynchronously_1_3 failed with " << code
|
||||
<< ": " << message;
|
||||
cb(V1_3::utils::convert(code).value(), V1_3::utils::convert(outputShapes).value(),
|
||||
V1_2::utils::kNoTiming);
|
||||
return Void();
|
||||
}
|
||||
auto [outputShapes, timing] = std::move(result).value();
|
||||
cb(V1_3::ErrorStatus::NONE, outputShapes, timing);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> PreparedModel::configureExecutionBurst(
|
||||
const sp<V1_2::IBurstCallback>& callback,
|
||||
const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
|
||||
const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
|
||||
configureExecutionBurst_cb cb) {
|
||||
const sp<V1_2::IBurstContext> burst = nn::ExecutionBurstServer::create(
|
||||
callback, requestChannel, resultChannel, this, std::chrono::microseconds{0});
|
||||
|
||||
if (burst == nullptr) {
|
||||
cb(V1_0::ErrorStatus::GENERAL_FAILURE, {});
|
||||
} else {
|
||||
cb(V1_0::ErrorStatus::NONE, burst);
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> PreparedModel::executeFenced(const V1_3::Request& request,
|
||||
const hidl_vec<hidl_handle>& waitFor,
|
||||
V1_2::MeasureTiming measure,
|
||||
const V1_3::OptionalTimePoint& deadline,
|
||||
const V1_3::OptionalTimeoutDuration& loopTimeoutDuration,
|
||||
const V1_3::OptionalTimeoutDuration& duration,
|
||||
executeFenced_cb callback) {
|
||||
auto result = adapter::executeFenced(kPreparedModel, request, waitFor, measure, deadline,
|
||||
loopTimeoutDuration, duration);
|
||||
if (!result.has_value()) {
|
||||
auto [message, code] = std::move(result).error();
|
||||
LOG(ERROR) << "adapter::PreparedModel::executeFenced failed with " << code << ": "
|
||||
<< message;
|
||||
callback(V1_3::utils::convert(code).value(), {}, nullptr);
|
||||
return Void();
|
||||
}
|
||||
auto [syncFence, executeFencedCallback] = std::move(result).value();
|
||||
callback(V1_3::ErrorStatus::NONE, syncFence, executeFencedCallback);
|
||||
return Void();
|
||||
}
|
||||
|
||||
} // namespace android::hardware::neuralnetworks::adapter
|
|
@ -71,8 +71,8 @@ nn::GeneralResult<std::reference_wrapper<const nn::Request>> flushDataFromPointe
|
|||
nn::GeneralResult<void> unflushDataFromSharedToPointer(
|
||||
const nn::Request& request, const std::optional<nn::Request>& maybeRequestInShared);
|
||||
|
||||
std::vector<uint32_t> countNumberOfConsumers(size_t numberOfOperands,
|
||||
const std::vector<nn::Operation>& operations);
|
||||
nn::GeneralResult<std::vector<uint32_t>> countNumberOfConsumers(
|
||||
size_t numberOfOperands, const std::vector<nn::Operation>& operations);
|
||||
|
||||
nn::GeneralResult<hidl_memory> createHidlMemoryFromSharedMemory(const nn::SharedMemory& memory);
|
||||
nn::GeneralResult<nn::SharedMemory> createSharedMemoryFromHidlMemory(const hidl_memory& memory);
|
||||
|
|
|
@ -246,9 +246,9 @@ nn::GeneralResult<void> unflushDataFromSharedToPointer(
|
|||
return {};
|
||||
}
|
||||
|
||||
std::vector<uint32_t> countNumberOfConsumers(size_t numberOfOperands,
|
||||
const std::vector<nn::Operation>& operations) {
|
||||
return nn::countNumberOfConsumers(numberOfOperands, operations);
|
||||
nn::GeneralResult<std::vector<uint32_t>> countNumberOfConsumers(
|
||||
size_t numberOfOperands, const std::vector<nn::Operation>& operations) {
|
||||
return makeGeneralFailure(nn::countNumberOfConsumers(numberOfOperands, operations));
|
||||
}
|
||||
|
||||
nn::GeneralResult<hidl_memory> createHidlMemoryFromSharedMemory(const nn::SharedMemory& memory) {
|
||||
|
|
Loading…
Reference in a new issue