mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 22:04:26 +00:00
Merge changes from topic "nnapi-reusable-execution-hal"
* changes: Reusable execution at HAL level -- HAL. Define AIDL reusable execution interface.
This commit is contained in:
@@ -411,7 +411,7 @@
|
|||||||
</hal>
|
</hal>
|
||||||
<hal format="aidl" optional="true">
|
<hal format="aidl" optional="true">
|
||||||
<name>android.hardware.neuralnetworks</name>
|
<name>android.hardware.neuralnetworks</name>
|
||||||
<version>1-3</version>
|
<version>1-4</version>
|
||||||
<interface>
|
<interface>
|
||||||
<name>IDevice</name>
|
<name>IDevice</name>
|
||||||
<regex-instance>.*</regex-instance>
|
<regex-instance>.*</regex-instance>
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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 IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||||
|
// two cases:
|
||||||
|
// 1). this is a frozen version file - do not edit this in any case.
|
||||||
|
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||||
|
// the interface (from the latest frozen version), the build system will
|
||||||
|
// prompt you to update this file with `m <name>-update-api`.
|
||||||
|
//
|
||||||
|
// You must not make a backward incompatible change to any AIDL file built
|
||||||
|
// with the aidl_interface module type with versions property set. The module
|
||||||
|
// type is used to build AIDL files in a way that they can be used across
|
||||||
|
// independently updatable components of the system. If a device is shipped
|
||||||
|
// with such a backward incompatible change, it has a high risk of breaking
|
||||||
|
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||||
|
|
||||||
|
package android.hardware.neuralnetworks;
|
||||||
|
@VintfStability
|
||||||
|
interface IExecution {
|
||||||
|
android.hardware.neuralnetworks.ExecutionResult executeSynchronously(in long deadlineNs);
|
||||||
|
android.hardware.neuralnetworks.FencedExecutionResult executeFenced(in ParcelFileDescriptor[] waitFor, in long deadlineNs, in long durationNs);
|
||||||
|
}
|
||||||
@@ -37,6 +37,7 @@ interface IPreparedModel {
|
|||||||
android.hardware.neuralnetworks.ExecutionResult executeSynchronously(in android.hardware.neuralnetworks.Request request, in boolean measureTiming, in long deadlineNs, in long loopTimeoutDurationNs);
|
android.hardware.neuralnetworks.ExecutionResult executeSynchronously(in android.hardware.neuralnetworks.Request request, in boolean measureTiming, in long deadlineNs, in long loopTimeoutDurationNs);
|
||||||
android.hardware.neuralnetworks.FencedExecutionResult executeFenced(in android.hardware.neuralnetworks.Request request, in ParcelFileDescriptor[] waitFor, in boolean measureTiming, in long deadlineNs, in long loopTimeoutDurationNs, in long durationNs);
|
android.hardware.neuralnetworks.FencedExecutionResult executeFenced(in android.hardware.neuralnetworks.Request request, in ParcelFileDescriptor[] waitFor, in boolean measureTiming, in long deadlineNs, in long loopTimeoutDurationNs, in long durationNs);
|
||||||
android.hardware.neuralnetworks.IBurst configureExecutionBurst();
|
android.hardware.neuralnetworks.IBurst configureExecutionBurst();
|
||||||
|
android.hardware.neuralnetworks.IExecution createReusableExecution(in android.hardware.neuralnetworks.Request request, in boolean measureTiming, in long loopTimeoutDurationNs);
|
||||||
const long DEFAULT_LOOP_TIMEOUT_DURATION_NS = 2000000000;
|
const long DEFAULT_LOOP_TIMEOUT_DURATION_NS = 2000000000;
|
||||||
const long MAXIMUM_LOOP_TIMEOUT_DURATION_NS = 15000000000;
|
const long MAXIMUM_LOOP_TIMEOUT_DURATION_NS = 15000000000;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,10 +78,10 @@ interface IBurst {
|
|||||||
* runs from the time the driver sees the call to the executeSynchronously
|
* runs from the time the driver sees the call to the executeSynchronously
|
||||||
* function to the time the driver returns from the function.
|
* function to the time the driver returns from the function.
|
||||||
* @param deadlineNs The time by which the execution is expected to complete. The time is
|
* @param deadlineNs The time by which the execution is expected to complete. The time is
|
||||||
* measured in nanoseconds since epoch of the steady clock (as from
|
* measured in nanoseconds since boot (as from clock_gettime(CLOCK_BOOTTIME,
|
||||||
* std::chrono::steady_clock). If the execution cannot be finished by the
|
* &ts) or ::android::base::boot_clock). If the execution cannot be finished
|
||||||
* deadline, the execution may be aborted. Passing -1 means the deadline is
|
* by the deadline, the execution may be aborted. Passing -1 means the
|
||||||
* omitted. Other negative values are invalid.
|
* deadline is omitted. Other negative values are invalid.
|
||||||
* @param loopTimeoutDurationNs The maximum amount of time in nanoseconds that should be spent
|
* @param loopTimeoutDurationNs The maximum amount of time in nanoseconds that should be spent
|
||||||
* executing a {@link OperationType::WHILE} operation. If a loop
|
* executing a {@link OperationType::WHILE} operation. If a loop
|
||||||
* condition model does not output false within this duration, the
|
* condition model does not output false within this duration, the
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.hardware.neuralnetworks;
|
||||||
|
|
||||||
|
import android.hardware.neuralnetworks.ExecutionResult;
|
||||||
|
import android.hardware.neuralnetworks.FencedExecutionResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IExecution represents a reusable execution object with request and most other execution
|
||||||
|
* properties fixed. It is used to launch executions.
|
||||||
|
*
|
||||||
|
* At most one execution may occur on a reusable execution object at any given time, either by
|
||||||
|
* means of executeSynchronously or executeFenced.
|
||||||
|
*
|
||||||
|
* An IExecution object is used to control a set of executions on the same prepared model with
|
||||||
|
* the same request and properties. IExecution objects enable some optimizations:
|
||||||
|
* (1) An IExecution object can preserve resources between executions. For example, a driver can
|
||||||
|
* map a memory object when the IExecution object is created and cache the mapping for reuse in
|
||||||
|
* subsequent executions. Any cached resource can be released when the IExecution object is
|
||||||
|
* destroyed.
|
||||||
|
* (2) Because an IExecution object may be used for at most one execution at a time, any transient
|
||||||
|
* execution resources such as intermediate tensors can be allocated once when the IExecution
|
||||||
|
* object is created and freed when the IExecution object is destroyed.
|
||||||
|
* (3) An IExecution object is created for a fixed request. This enables the implementation to apply
|
||||||
|
* request-specific optimizations. For example, an implementation can avoid request validation
|
||||||
|
* and conversions when the IExecution object is reused. An implementation may also choose to
|
||||||
|
* specialize the dynamic tensor shapes in the IExecution object according to the request.
|
||||||
|
*/
|
||||||
|
@VintfStability
|
||||||
|
interface IExecution {
|
||||||
|
/**
|
||||||
|
* Performs a synchronous execution on the reusable execution object.
|
||||||
|
*
|
||||||
|
* The execution is performed synchronously with respect to the caller. executeSynchronously
|
||||||
|
* must verify the inputs to the function are correct, and the usages of memory pools allocated
|
||||||
|
* by IDevice::allocate are valid. If there is an error, executeSynchronously must immediately
|
||||||
|
* return a service specific exception with the appropriate ErrorStatus value. If the inputs to
|
||||||
|
* the function are valid and there is no error, executeSynchronously must perform the
|
||||||
|
* execution, and must not return until the execution is complete.
|
||||||
|
*
|
||||||
|
* The caller must not change the content of any data object referenced by the 'request'
|
||||||
|
* provided in {@link IPreparedModel::createReusableExecution} (described by the
|
||||||
|
* {@link DataLocation} of a {@link RequestArgument}) until executeSynchronously returns.
|
||||||
|
* executeSynchronously must not change the content of any of the data objects corresponding to
|
||||||
|
* 'request' inputs.
|
||||||
|
*
|
||||||
|
* If the execution object was configured from a prepared model wherein all tensor operands have
|
||||||
|
* fully specified dimensions, and the inputs to the function are valid, and at execution time
|
||||||
|
* every operation's input operands have legal values, then the execution should complete
|
||||||
|
* successfully: there must be no failure unless the device itself is in a bad state.
|
||||||
|
*
|
||||||
|
* If the execution object was created with measureTiming being true and the execution is
|
||||||
|
* successful, the driver may report the timing information in the returning
|
||||||
|
* {@link ExecutionResult}. The duration runs from the time the driver sees the call to the time
|
||||||
|
* the driver returns from the function.
|
||||||
|
*
|
||||||
|
* executeSynchronously may be called with an optional deadline. If the execution is not able to
|
||||||
|
* be completed before the provided deadline, the execution may be aborted, and either
|
||||||
|
* {@link ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
|
||||||
|
* ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due to an abort must be
|
||||||
|
* sent the same way as other errors, described above.
|
||||||
|
*
|
||||||
|
* @param deadlineNs The time by which the execution is expected to complete. The time is
|
||||||
|
* measured in nanoseconds since boot (as from clock_gettime(CLOCK_BOOTTIME,
|
||||||
|
* &ts) or ::android::base::boot_clock). If the execution cannot be finished
|
||||||
|
* by the deadline, the execution may be aborted. Passing -1 means the
|
||||||
|
* deadline is omitted. Other negative values are invalid.
|
||||||
|
* @return ExecutionResult parcelable, containing the status of the execution, output shapes
|
||||||
|
* and timing information.
|
||||||
|
* @throws ServiceSpecificException with one of the following ErrorStatus values:
|
||||||
|
* - DEVICE_UNAVAILABLE if driver is offline or busy
|
||||||
|
* - GENERAL_FAILURE if there is an unspecified error
|
||||||
|
* - INVALID_ARGUMENT if one of the input arguments is invalid
|
||||||
|
* - MISSED_DEADLINE_* if the execution is aborted because it cannot be completed by the
|
||||||
|
* deadline
|
||||||
|
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
|
||||||
|
*/
|
||||||
|
ExecutionResult executeSynchronously(in long deadlineNs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launch a fenced asynchronous execution on the reusable execution object.
|
||||||
|
*
|
||||||
|
* The execution is performed asynchronously with respect to the caller. executeFenced must
|
||||||
|
* verify the inputs to the function are correct, and the usages of memory pools allocated by
|
||||||
|
* IDevice::allocate are valid. If there is an error, executeFenced must immediately return a
|
||||||
|
* service specific exception with the corresponding ErrorStatus. If the inputs to the function
|
||||||
|
* are valid and there is no error, executeFenced must dispatch an asynchronous task to perform
|
||||||
|
* the execution in the background, and immediately return a {@link FencedExecutionResult}
|
||||||
|
* containing two fields: a callback (which can be used by the client to query the duration and
|
||||||
|
* runtime error status) and a sync fence (which will be signaled once the execution is
|
||||||
|
* completed). If the task has finished before the call returns, syncFence file descriptor may
|
||||||
|
* be set to -1. The execution must wait for all the sync fences (if any) in waitFor to be
|
||||||
|
* signaled before starting the actual execution.
|
||||||
|
*
|
||||||
|
* When the asynchronous task has finished its execution, it must immediately signal the
|
||||||
|
* syncFence returned from the executeFenced call. After the syncFence is signaled, the task
|
||||||
|
* must not modify the content of any data object referenced by the 'request' provided in
|
||||||
|
* IPreparedModel::createReusableExecution (described by the {@link DataLocation} of a
|
||||||
|
* {@link RequestArgument}).
|
||||||
|
*
|
||||||
|
* executeFenced may be called with an optional deadline and an optional duration. If the
|
||||||
|
* execution is not able to be completed before the provided deadline or within the timeout
|
||||||
|
* duration (measured from when all sync fences in waitFor are signaled), whichever comes
|
||||||
|
* earlier, the execution may be aborted, and either
|
||||||
|
* {@link ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
|
||||||
|
* ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due to an abort must be
|
||||||
|
* sent the same way as other errors, described above.
|
||||||
|
*
|
||||||
|
* If any of the sync fences in waitFor changes to error status after the executeFenced call
|
||||||
|
* succeeds, or the execution is aborted because it cannot finish before the deadline has been
|
||||||
|
* reached or the duration has elapsed, the driver must immediately set the returned syncFence
|
||||||
|
* to error status.
|
||||||
|
*
|
||||||
|
* @param waitFor A vector of sync fence file descriptors. Execution must not start until all
|
||||||
|
* sync fences have been signaled.
|
||||||
|
* @param deadlineNs The time by which the execution is expected to complete. The time is
|
||||||
|
* measured in nanoseconds since boot (as from clock_gettime(CLOCK_BOOTTIME,
|
||||||
|
* &ts) or ::android::base::boot_clock). If the execution cannot be finished
|
||||||
|
* by the deadline, the execution may be aborted. Passing -1 means the
|
||||||
|
* deadline is omitted. Other negative values are invalid.
|
||||||
|
* @param durationNs The length of time in nanoseconds within which the execution is expected
|
||||||
|
* to complete after all sync fences in waitFor are signaled. If the
|
||||||
|
* execution cannot be finished within the duration, the execution may be
|
||||||
|
* aborted. Passing -1 means the duration is omitted. Other negative values
|
||||||
|
* are invalid.
|
||||||
|
* @return The FencedExecutionResult parcelable, containing IFencedExecutionCallback and the
|
||||||
|
* sync fence.
|
||||||
|
* @throws ServiceSpecificException with one of the following ErrorStatus values:
|
||||||
|
* - DEVICE_UNAVAILABLE if driver is offline or busy
|
||||||
|
* - GENERAL_FAILURE if there is an unspecified error
|
||||||
|
* - INVALID_ARGUMENT if one of the input arguments is invalid, including fences in error
|
||||||
|
* states.
|
||||||
|
* - MISSED_DEADLINE_* if the execution is aborted because it cannot be completed by the
|
||||||
|
* deadline
|
||||||
|
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
|
||||||
|
*/
|
||||||
|
FencedExecutionResult executeFenced(
|
||||||
|
in ParcelFileDescriptor[] waitFor, in long deadlineNs, in long durationNs);
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ import android.hardware.neuralnetworks.ErrorStatus;
|
|||||||
import android.hardware.neuralnetworks.ExecutionResult;
|
import android.hardware.neuralnetworks.ExecutionResult;
|
||||||
import android.hardware.neuralnetworks.FencedExecutionResult;
|
import android.hardware.neuralnetworks.FencedExecutionResult;
|
||||||
import android.hardware.neuralnetworks.IBurst;
|
import android.hardware.neuralnetworks.IBurst;
|
||||||
|
import android.hardware.neuralnetworks.IExecution;
|
||||||
import android.hardware.neuralnetworks.Request;
|
import android.hardware.neuralnetworks.Request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,11 +106,12 @@ interface IPreparedModel {
|
|||||||
* IDevice::allocate are valid. If there is an error, executeFenced must immediately return a
|
* IDevice::allocate are valid. If there is an error, executeFenced must immediately return a
|
||||||
* service specific exception with the corresponding ErrorStatus. If the inputs to the function
|
* service specific exception with the corresponding ErrorStatus. If the inputs to the function
|
||||||
* are valid and there is no error, executeFenced must dispatch an asynchronous task to perform
|
* are valid and there is no error, executeFenced must dispatch an asynchronous task to perform
|
||||||
* the execution in the background, assign a sync fence that will be signaled once the execution
|
* the execution in the background, and immediately return a {@link FencedExecutionResult}
|
||||||
* is completed and immediately return a callback that can be used by the client to query the
|
* containing two fields: a callback (which can be used by the client to query the duration and
|
||||||
* duration and runtime error status. If the task has finished before the call returns,
|
* runtime error status) and a sync fence (which will be signaled once the execution is
|
||||||
* syncFence file descriptor may be set to -1. The execution must wait for all the sync fences
|
* completed). If the task has finished before the call returns, syncFence file descriptor may
|
||||||
* (if any) in waitFor to be signaled before starting the actual execution.
|
* be set to -1. The execution must wait for all the sync fences (if any) in waitFor to be
|
||||||
|
* signaled before starting the actual execution.
|
||||||
*
|
*
|
||||||
* When the asynchronous task has finished its execution, it must immediately signal the
|
* When the asynchronous task has finished its execution, it must immediately signal the
|
||||||
* syncFence returned from the executeFenced call. After the syncFence is signaled, the task
|
* syncFence returned from the executeFenced call. After the syncFence is signaled, the task
|
||||||
@@ -186,4 +188,36 @@ interface IPreparedModel {
|
|||||||
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
|
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
|
||||||
*/
|
*/
|
||||||
IBurst configureExecutionBurst();
|
IBurst configureExecutionBurst();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a reusable execution object to launch multiple executions with the same request and
|
||||||
|
* properties.
|
||||||
|
*
|
||||||
|
* createReusableExecution must verify the inputs to the function are correct, and the usages of
|
||||||
|
* memory pools allocated by IDevice::allocate are valid. If there is an error,
|
||||||
|
* createReusableExecution must immediately return a service specific exception with the
|
||||||
|
* appropriate ErrorStatus value. If the inputs to the function are valid and there is no error,
|
||||||
|
* createReusableExecution must construct a reusable execution.
|
||||||
|
*
|
||||||
|
* @param request The input and output information on which the prepared model is to be
|
||||||
|
* executed.
|
||||||
|
* @param measure Specifies whether or not to measure duration of the execution.
|
||||||
|
* @param loopTimeoutDurationNs The maximum amount of time in nanoseconds that should be spent
|
||||||
|
* executing a {@link OperationType::WHILE} operation. If a loop
|
||||||
|
* condition model does not output false within this duration, the
|
||||||
|
* computation performed on the returned reusable execution object
|
||||||
|
* must be aborted. If -1 is provided, the maximum amount
|
||||||
|
* of time is {@link DEFAULT_LOOP_TIMEOUT_DURATION_NS}. Other
|
||||||
|
* negative values are invalid. When provided, the duration must
|
||||||
|
* not exceed {@link MAXIMUM_LOOP_TIMEOUT_DURATION_NS}.
|
||||||
|
* @return execution An IExecution object representing a reusable execution that has been
|
||||||
|
* specialized for a fixed request.
|
||||||
|
* @throws ServiceSpecificException with one of the following ErrorStatus values:
|
||||||
|
* - DEVICE_UNAVAILABLE if driver is offline or busy
|
||||||
|
* - GENERAL_FAILURE if there is an unspecified error
|
||||||
|
* - INVALID_ARGUMENT if one of the input arguments is invalid
|
||||||
|
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
|
||||||
|
*/
|
||||||
|
IExecution createReusableExecution(
|
||||||
|
in Request request, in boolean measureTiming, in long loopTimeoutDurationNs);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,14 @@ package {
|
|||||||
cc_defaults {
|
cc_defaults {
|
||||||
name: "neuralnetworks_utils_hal_aidl_defaults",
|
name: "neuralnetworks_utils_hal_aidl_defaults",
|
||||||
defaults: ["neuralnetworks_utils_defaults"],
|
defaults: ["neuralnetworks_utils_defaults"],
|
||||||
srcs: ["src/*"],
|
srcs: [
|
||||||
|
// AIDL utils that a driver may depend on.
|
||||||
|
"src/BufferTracker.cpp",
|
||||||
|
"src/Conversions.cpp",
|
||||||
|
"src/HalUtils.cpp",
|
||||||
|
"src/Utils.cpp",
|
||||||
|
"src/ValidateHal.cpp",
|
||||||
|
],
|
||||||
local_include_dirs: ["include/nnapi/hal/aidl/"],
|
local_include_dirs: ["include/nnapi/hal/aidl/"],
|
||||||
export_include_dirs: ["include"],
|
export_include_dirs: ["include"],
|
||||||
cflags: ["-Wthread-safety"],
|
cflags: ["-Wthread-safety"],
|
||||||
@@ -47,6 +54,7 @@ cc_defaults {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated. Remove once all modules depending on this are migrated away.
|
||||||
cc_library_static {
|
cc_library_static {
|
||||||
name: "neuralnetworks_utils_hal_aidl_v1",
|
name: "neuralnetworks_utils_hal_aidl_v1",
|
||||||
defaults: ["neuralnetworks_utils_hal_aidl_defaults"],
|
defaults: ["neuralnetworks_utils_hal_aidl_defaults"],
|
||||||
@@ -55,20 +63,26 @@ cc_library_static {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
cc_library_static {
|
|
||||||
name: "neuralnetworks_utils_hal_aidl_v2",
|
|
||||||
defaults: ["neuralnetworks_utils_hal_aidl_defaults"],
|
|
||||||
shared_libs: [
|
|
||||||
"android.hardware.neuralnetworks-V2-ndk",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_library_static {
|
cc_library_static {
|
||||||
name: "neuralnetworks_utils_hal_aidl",
|
name: "neuralnetworks_utils_hal_aidl",
|
||||||
defaults: ["neuralnetworks_utils_hal_aidl_defaults"],
|
defaults: ["neuralnetworks_utils_hal_aidl_defaults"],
|
||||||
shared_libs: [
|
srcs: [
|
||||||
"android.hardware.neuralnetworks-V3-ndk",
|
// Additional AIDL utils for the runtime.
|
||||||
|
"src/Assertions.cpp",
|
||||||
|
"src/Buffer.cpp",
|
||||||
|
"src/Burst.cpp",
|
||||||
|
"src/Callbacks.cpp",
|
||||||
|
"src/Device.cpp",
|
||||||
|
"src/Execution.cpp",
|
||||||
|
"src/InvalidDevice.cpp",
|
||||||
|
"src/PreparedModel.cpp",
|
||||||
|
"src/ProtectCallback.cpp",
|
||||||
|
"src/Service.cpp",
|
||||||
],
|
],
|
||||||
|
shared_libs: [
|
||||||
|
"android.hardware.neuralnetworks-V4-ndk",
|
||||||
|
],
|
||||||
|
cflags: ["-DNN_AIDL_V4_OR_ABOVE"],
|
||||||
}
|
}
|
||||||
|
|
||||||
// A cc_defaults that includes the latest non-experimental AIDL utilities and other AIDL libraries
|
// A cc_defaults that includes the latest non-experimental AIDL utilities and other AIDL libraries
|
||||||
@@ -79,9 +93,10 @@ cc_defaults {
|
|||||||
static_libs: [
|
static_libs: [
|
||||||
"android.hardware.common-V2-ndk",
|
"android.hardware.common-V2-ndk",
|
||||||
"android.hardware.graphics.common-V2-ndk",
|
"android.hardware.graphics.common-V2-ndk",
|
||||||
"android.hardware.neuralnetworks-V3-ndk",
|
"android.hardware.neuralnetworks-V4-ndk",
|
||||||
"neuralnetworks_utils_hal_aidl",
|
"neuralnetworks_utils_hal_aidl",
|
||||||
],
|
],
|
||||||
|
cflags: ["-DNN_AIDL_V4_OR_ABOVE"],
|
||||||
}
|
}
|
||||||
|
|
||||||
cc_test {
|
cc_test {
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ class PreparedModelCallback final : public BnPreparedModelCallback, public IProt
|
|||||||
public:
|
public:
|
||||||
using Data = nn::GeneralResult<nn::SharedPreparedModel>;
|
using Data = nn::GeneralResult<nn::SharedPreparedModel>;
|
||||||
|
|
||||||
|
PreparedModelCallback(nn::Version featureLevel) : kFeatureLevel(featureLevel) {}
|
||||||
|
|
||||||
ndk::ScopedAStatus notify(ErrorStatus status,
|
ndk::ScopedAStatus notify(ErrorStatus status,
|
||||||
const std::shared_ptr<IPreparedModel>& preparedModel) override;
|
const std::shared_ptr<IPreparedModel>& preparedModel) override;
|
||||||
|
|
||||||
@@ -44,6 +46,7 @@ class PreparedModelCallback final : public BnPreparedModelCallback, public IProt
|
|||||||
Data get();
|
Data get();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
const nn::Version kFeatureLevel;
|
||||||
hal::utils::TransferValue<Data> mData;
|
hal::utils::TransferValue<Data> mData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_EXECUTION_H
|
#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_EXECUTION_H
|
||||||
#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_EXECUTION_H
|
#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_EXECUTION_H
|
||||||
|
|
||||||
|
#include <aidl/android/hardware/neuralnetworks/IExecution.h>
|
||||||
|
|
||||||
#include <nnapi/IExecution.h>
|
#include <nnapi/IExecution.h>
|
||||||
#include <nnapi/Result.h>
|
#include <nnapi/Result.h>
|
||||||
#include <nnapi/Types.h>
|
#include <nnapi/Types.h>
|
||||||
@@ -33,17 +35,22 @@
|
|||||||
|
|
||||||
namespace aidl::android::hardware::neuralnetworks::utils {
|
namespace aidl::android::hardware::neuralnetworks::utils {
|
||||||
|
|
||||||
class Execution final : public nn::IExecution, public std::enable_shared_from_this<Execution> {
|
// A reusable execution implementation with a cached Request, internally it is still passing the
|
||||||
|
// request to the driver in every computation.
|
||||||
|
class ExecutionWithCachedRequest final
|
||||||
|
: public nn::IExecution,
|
||||||
|
public std::enable_shared_from_this<ExecutionWithCachedRequest> {
|
||||||
struct PrivateConstructorTag {};
|
struct PrivateConstructorTag {};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static nn::GeneralResult<std::shared_ptr<const Execution>> create(
|
static nn::GeneralResult<std::shared_ptr<const ExecutionWithCachedRequest>> create(
|
||||||
std::shared_ptr<const PreparedModel> preparedModel, Request request,
|
std::shared_ptr<const PreparedModel> preparedModel, Request request,
|
||||||
hal::utils::RequestRelocation relocation, bool measure, int64_t loopTimeoutDuration);
|
hal::utils::RequestRelocation relocation, bool measure, int64_t loopTimeoutDuration);
|
||||||
|
|
||||||
Execution(PrivateConstructorTag tag, std::shared_ptr<const PreparedModel> preparedModel,
|
ExecutionWithCachedRequest(PrivateConstructorTag tag,
|
||||||
Request request, hal::utils::RequestRelocation relocation, bool measure,
|
std::shared_ptr<const PreparedModel> preparedModel, Request request,
|
||||||
int64_t loopTimeoutDuration);
|
hal::utils::RequestRelocation relocation, bool measure,
|
||||||
|
int64_t loopTimeoutDuration);
|
||||||
|
|
||||||
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
|
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
|
||||||
const nn::OptionalTimePoint& deadline) const override;
|
const nn::OptionalTimePoint& deadline) const override;
|
||||||
@@ -60,6 +67,30 @@ class Execution final : public nn::IExecution, public std::enable_shared_from_th
|
|||||||
const int64_t kLoopTimeoutDuration;
|
const int64_t kLoopTimeoutDuration;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// A reusable execution implementation that is backed by an actual AIDL IExecution object.
|
||||||
|
class Execution final : public nn::IExecution, public std::enable_shared_from_this<Execution> {
|
||||||
|
struct PrivateConstructorTag {};
|
||||||
|
|
||||||
|
public:
|
||||||
|
static nn::GeneralResult<std::shared_ptr<const Execution>> create(
|
||||||
|
std::shared_ptr<aidl_hal::IExecution> execution,
|
||||||
|
hal::utils::RequestRelocation relocation);
|
||||||
|
|
||||||
|
Execution(PrivateConstructorTag tag, std::shared_ptr<aidl_hal::IExecution> execution,
|
||||||
|
hal::utils::RequestRelocation relocation);
|
||||||
|
|
||||||
|
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
|
||||||
|
const nn::OptionalTimePoint& deadline) const override;
|
||||||
|
|
||||||
|
nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
|
||||||
|
const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
|
||||||
|
const nn::OptionalDuration& timeoutDurationAfterFence) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::shared_ptr<aidl_hal::IExecution> kExecution;
|
||||||
|
const hal::utils::RequestRelocation kRelocation;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace aidl::android::hardware::neuralnetworks::utils
|
} // namespace aidl::android::hardware::neuralnetworks::utils
|
||||||
|
|
||||||
#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_EXECUTION_H
|
#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_EXECUTION_H
|
||||||
|
|||||||
@@ -61,6 +61,11 @@
|
|||||||
#include <aidl/android/hardware/neuralnetworks/SymmPerChannelQuantParams.h>
|
#include <aidl/android/hardware/neuralnetworks/SymmPerChannelQuantParams.h>
|
||||||
#include <aidl/android/hardware/neuralnetworks/Timing.h>
|
#include <aidl/android/hardware/neuralnetworks/Timing.h>
|
||||||
|
|
||||||
|
#ifdef NN_AIDL_V4_OR_ABOVE
|
||||||
|
#include <aidl/android/hardware/neuralnetworks/BnExecution.h>
|
||||||
|
#include <aidl/android/hardware/neuralnetworks/IExecution.h>
|
||||||
|
#endif // NN_AIDL_V4_OR_ABOVE
|
||||||
|
|
||||||
namespace android::nn {
|
namespace android::nn {
|
||||||
|
|
||||||
namespace aidl_hal = ::aidl::android::hardware::neuralnetworks;
|
namespace aidl_hal = ::aidl::android::hardware::neuralnetworks;
|
||||||
|
|||||||
@@ -41,10 +41,11 @@ class PreparedModel final : public nn::IPreparedModel,
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
static nn::GeneralResult<std::shared_ptr<const PreparedModel>> create(
|
static nn::GeneralResult<std::shared_ptr<const PreparedModel>> create(
|
||||||
std::shared_ptr<aidl_hal::IPreparedModel> preparedModel);
|
std::shared_ptr<aidl_hal::IPreparedModel> preparedModel, nn::Version featureLevel);
|
||||||
|
|
||||||
PreparedModel(PrivateConstructorTag tag,
|
PreparedModel(PrivateConstructorTag tag,
|
||||||
std::shared_ptr<aidl_hal::IPreparedModel> preparedModel);
|
std::shared_ptr<aidl_hal::IPreparedModel> preparedModel,
|
||||||
|
nn::Version featureLevel);
|
||||||
|
|
||||||
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
|
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
|
||||||
const nn::Request& request, nn::MeasureTiming measure,
|
const nn::Request& request, nn::MeasureTiming measure,
|
||||||
@@ -78,6 +79,7 @@ class PreparedModel final : public nn::IPreparedModel,
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
const std::shared_ptr<aidl_hal::IPreparedModel> kPreparedModel;
|
const std::shared_ptr<aidl_hal::IPreparedModel> kPreparedModel;
|
||||||
|
const nn::Version kFeatureLevel;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace aidl::android::hardware::neuralnetworks::utils
|
} // namespace aidl::android::hardware::neuralnetworks::utils
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ constexpr std::optional<nn::Version> aidlVersionToCanonicalVersion(int aidlVersi
|
|||||||
return nn::kVersionFeatureLevel6;
|
return nn::kVersionFeatureLevel6;
|
||||||
case 3:
|
case 3:
|
||||||
return nn::kVersionFeatureLevel7;
|
return nn::kVersionFeatureLevel7;
|
||||||
|
case 4:
|
||||||
|
return nn::kVersionFeatureLevel8;
|
||||||
default:
|
default:
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,16 +38,17 @@ namespace {
|
|||||||
// nn::kVersionFeatureLevel5. On failure, this function returns with the appropriate
|
// nn::kVersionFeatureLevel5. On failure, this function returns with the appropriate
|
||||||
// nn::GeneralError.
|
// nn::GeneralError.
|
||||||
nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
|
nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
|
||||||
ErrorStatus status, const std::shared_ptr<IPreparedModel>& preparedModel) {
|
ErrorStatus status, const std::shared_ptr<IPreparedModel>& preparedModel,
|
||||||
|
nn::Version featureLevel) {
|
||||||
HANDLE_STATUS_AIDL(status) << "model preparation failed with " << toString(status);
|
HANDLE_STATUS_AIDL(status) << "model preparation failed with " << toString(status);
|
||||||
return NN_TRY(PreparedModel::create(preparedModel));
|
return NN_TRY(PreparedModel::create(preparedModel, featureLevel));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
ndk::ScopedAStatus PreparedModelCallback::notify(
|
ndk::ScopedAStatus PreparedModelCallback::notify(
|
||||||
ErrorStatus status, const std::shared_ptr<IPreparedModel>& preparedModel) {
|
ErrorStatus status, const std::shared_ptr<IPreparedModel>& preparedModel) {
|
||||||
mData.put(prepareModelCallback(status, preparedModel));
|
mData.put(prepareModelCallback(status, preparedModel, kFeatureLevel));
|
||||||
return ndk::ScopedAStatus::ok();
|
return ndk::ScopedAStatus::ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -229,7 +229,7 @@ nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel(
|
|||||||
const auto aidlDataCache = NN_TRY(convert(dataCache));
|
const auto aidlDataCache = NN_TRY(convert(dataCache));
|
||||||
const auto aidlToken = NN_TRY(convert(token));
|
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 scoped = kDeathHandler.protectCallback(cb.get());
|
||||||
|
|
||||||
const auto ret = kDevice->prepareModel(aidlModel, aidlPreference, aidlPriority, aidlDeadline,
|
const auto ret = kDevice->prepareModel(aidlModel, aidlPreference, aidlPriority, aidlDeadline,
|
||||||
@@ -247,7 +247,7 @@ nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModelFromCache(
|
|||||||
const auto aidlDataCache = NN_TRY(convert(dataCache));
|
const auto aidlDataCache = NN_TRY(convert(dataCache));
|
||||||
const auto aidlToken = NN_TRY(convert(token));
|
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 scoped = kDeathHandler.protectCallback(cb.get());
|
||||||
|
|
||||||
const auto ret = kDevice->prepareModelFromCache(aidlDeadline, aidlModelCache, aidlDataCache,
|
const auto ret = kDevice->prepareModelFromCache(aidlDeadline, aidlModelCache, aidlDataCache,
|
||||||
|
|||||||
@@ -35,36 +35,39 @@
|
|||||||
|
|
||||||
namespace aidl::android::hardware::neuralnetworks::utils {
|
namespace aidl::android::hardware::neuralnetworks::utils {
|
||||||
|
|
||||||
nn::GeneralResult<std::shared_ptr<const Execution>> Execution::create(
|
nn::GeneralResult<std::shared_ptr<const ExecutionWithCachedRequest>>
|
||||||
std::shared_ptr<const PreparedModel> preparedModel, Request request,
|
ExecutionWithCachedRequest::create(std::shared_ptr<const PreparedModel> preparedModel,
|
||||||
hal::utils::RequestRelocation relocation, bool measure, int64_t loopTimeoutDuration) {
|
Request request, hal::utils::RequestRelocation relocation,
|
||||||
|
bool measure, int64_t loopTimeoutDuration) {
|
||||||
if (preparedModel == nullptr) {
|
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),
|
return std::make_shared<const ExecutionWithCachedRequest>(
|
||||||
std::move(request), std::move(relocation), measure,
|
PrivateConstructorTag{}, std::move(preparedModel), std::move(request),
|
||||||
loopTimeoutDuration);
|
std::move(relocation), measure, loopTimeoutDuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
Execution::Execution(PrivateConstructorTag /*tag*/,
|
ExecutionWithCachedRequest::ExecutionWithCachedRequest(
|
||||||
std::shared_ptr<const PreparedModel> preparedModel, Request request,
|
PrivateConstructorTag /*tag*/, std::shared_ptr<const PreparedModel> preparedModel,
|
||||||
hal::utils::RequestRelocation relocation, bool measure,
|
Request request, hal::utils::RequestRelocation relocation, bool measure,
|
||||||
int64_t loopTimeoutDuration)
|
int64_t loopTimeoutDuration)
|
||||||
: kPreparedModel(std::move(preparedModel)),
|
: kPreparedModel(std::move(preparedModel)),
|
||||||
kRequest(std::move(request)),
|
kRequest(std::move(request)),
|
||||||
kRelocation(std::move(relocation)),
|
kRelocation(std::move(relocation)),
|
||||||
kMeasure(measure),
|
kMeasure(measure),
|
||||||
kLoopTimeoutDuration(loopTimeoutDuration) {}
|
kLoopTimeoutDuration(loopTimeoutDuration) {}
|
||||||
|
|
||||||
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Execution::compute(
|
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
|
||||||
const nn::OptionalTimePoint& deadline) const {
|
ExecutionWithCachedRequest::compute(const nn::OptionalTimePoint& deadline) const {
|
||||||
const auto aidlDeadline = NN_TRY(convert(deadline));
|
const auto aidlDeadline = NN_TRY(convert(deadline));
|
||||||
return kPreparedModel->executeInternal(kRequest, kMeasure, aidlDeadline, kLoopTimeoutDuration,
|
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 std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
|
||||||
const nn::OptionalDuration& timeoutDurationAfterFence) const {
|
const nn::OptionalDuration& timeoutDurationAfterFence) const {
|
||||||
const auto aidlWaitFor = NN_TRY(convert(waitFor));
|
const auto aidlWaitFor = NN_TRY(convert(waitFor));
|
||||||
@@ -75,4 +78,18 @@ nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> Execu
|
|||||||
aidlTimeoutDurationAfterFence, kRelocation);
|
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
|
} // namespace aidl::android::hardware::neuralnetworks::utils
|
||||||
|
|||||||
@@ -54,21 +54,77 @@ nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> convertFencedExecutionResul
|
|||||||
return std::make_pair(NN_TRY(nn::convert(timingLaunched)), NN_TRY(nn::convert(timingFenced)));
|
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
|
} // namespace
|
||||||
|
|
||||||
nn::GeneralResult<std::shared_ptr<const PreparedModel>> PreparedModel::create(
|
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) {
|
if (preparedModel == nullptr) {
|
||||||
return NN_ERROR()
|
return NN_ERROR()
|
||||||
<< "aidl_hal::utils::PreparedModel::create must have non-null preparedModel";
|
<< "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*/,
|
PreparedModel::PreparedModel(PrivateConstructorTag /*tag*/,
|
||||||
std::shared_ptr<aidl_hal::IPreparedModel> preparedModel)
|
std::shared_ptr<aidl_hal::IPreparedModel> preparedModel,
|
||||||
: kPreparedModel(std::move(preparedModel)) {}
|
nn::Version featureLevel)
|
||||||
|
: kPreparedModel(std::move(preparedModel)), kFeatureLevel(featureLevel) {}
|
||||||
|
|
||||||
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> PreparedModel::execute(
|
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> PreparedModel::execute(
|
||||||
const nn::Request& request, nn::MeasureTiming measure,
|
const nn::Request& request, nn::MeasureTiming measure,
|
||||||
@@ -101,19 +157,7 @@ PreparedModel::executeInternal(const Request& request, bool measure, int64_t dea
|
|||||||
const auto ret = kPreparedModel->executeSynchronously(request, measure, deadline,
|
const auto ret = kPreparedModel->executeSynchronously(request, measure, deadline,
|
||||||
loopTimeoutDuration, &executionResult);
|
loopTimeoutDuration, &executionResult);
|
||||||
HANDLE_ASTATUS(ret) << "executeSynchronously failed";
|
HANDLE_ASTATUS(ret) << "executeSynchronously failed";
|
||||||
if (!executionResult.outputSufficientSize) {
|
return handleExecutionResult(executionResult, relocation);
|
||||||
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(convertExecutionResults(executionResult.outputShapes, executionResult.timing));
|
|
||||||
|
|
||||||
if (relocation.output) {
|
|
||||||
relocation.output->flush();
|
|
||||||
}
|
|
||||||
return std::make_pair(std::move(outputShapes), timing);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
|
nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
|
||||||
@@ -154,39 +198,7 @@ PreparedModel::executeFencedInternal(const Request& request,
|
|||||||
kPreparedModel->executeFenced(request, waitFor, measure, deadline, loopTimeoutDuration,
|
kPreparedModel->executeFenced(request, waitFor, measure, deadline, loopTimeoutDuration,
|
||||||
timeoutDurationAfterFence, &result);
|
timeoutDurationAfterFence, &result);
|
||||||
HANDLE_ASTATUS(ret) << "executeFenced failed";
|
HANDLE_ASTATUS(ret) << "executeFenced failed";
|
||||||
|
return handleFencedExecutionResult(result, 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 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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution(
|
nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution(
|
||||||
@@ -202,8 +214,18 @@ nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution(
|
|||||||
auto aidlRequest = NN_TRY(convert(requestInShared));
|
auto aidlRequest = NN_TRY(convert(requestInShared));
|
||||||
auto aidlMeasure = NN_TRY(convert(measure));
|
auto aidlMeasure = NN_TRY(convert(measure));
|
||||||
auto aidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
|
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;
|
||||||
|
const auto ret = kPreparedModel->createReusableExecution(
|
||||||
|
aidlRequest, aidlMeasure, aidlLoopTimeoutDuration, &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 {
|
nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
|
||||||
@@ -218,4 +240,36 @@ std::any PreparedModel::getUnderlyingResource() const {
|
|||||||
return resource;
|
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
|
} // namespace aidl::android::hardware::neuralnetworks::utils
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "MockBuffer.h"
|
#include "MockBuffer.h"
|
||||||
#include "MockDevice.h"
|
#include "MockDevice.h"
|
||||||
#include "MockPreparedModel.h"
|
#include "MockPreparedModel.h"
|
||||||
|
#include "TestUtils.h"
|
||||||
|
|
||||||
#include <aidl/android/hardware/neuralnetworks/BnDevice.h>
|
#include <aidl/android/hardware/neuralnetworks/BnDevice.h>
|
||||||
#include <android/binder_auto_utils.h>
|
#include <android/binder_auto_utils.h>
|
||||||
@@ -146,26 +147,7 @@ constexpr auto makeDeadObjectFailure = [] {
|
|||||||
return ndk::ScopedAStatus::fromStatus(STATUS_DEAD_OBJECT);
|
return ndk::ScopedAStatus::fromStatus(STATUS_DEAD_OBJECT);
|
||||||
};
|
};
|
||||||
|
|
||||||
class DeviceTest : public ::testing::TestWithParam<nn::Version> {
|
class DeviceTest : public VersionedAidlUtilsTestBase {};
|
||||||
protected:
|
|
||||||
const nn::Version kVersion = GetParam();
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string printDeviceTest(const testing::TestParamInfo<nn::Version>& info) {
|
|
||||||
const nn::Version version = info.param;
|
|
||||||
CHECK(!version.runtimeOnlyFeatures);
|
|
||||||
switch (version.level) {
|
|
||||||
case nn::Version::Level::FEATURE_LEVEL_5:
|
|
||||||
return "v1";
|
|
||||||
case nn::Version::Level::FEATURE_LEVEL_6:
|
|
||||||
return "v2";
|
|
||||||
case nn::Version::Level::FEATURE_LEVEL_7:
|
|
||||||
return "v3";
|
|
||||||
default:
|
|
||||||
LOG(FATAL) << "Invalid AIDL version: " << version;
|
|
||||||
return "invalid";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@@ -894,9 +876,6 @@ TEST_P(DeviceTest, allocateDeadObject) {
|
|||||||
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
|
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(TestDevice, DeviceTest,
|
INSTANTIATE_VERSIONED_AIDL_UTILS_TEST(DeviceTest, kAllAidlVersions);
|
||||||
::testing::Values(nn::kVersionFeatureLevel5, nn::kVersionFeatureLevel6,
|
|
||||||
nn::kVersionFeatureLevel7),
|
|
||||||
printDeviceTest);
|
|
||||||
|
|
||||||
} // namespace aidl::android::hardware::neuralnetworks::utils
|
} // namespace aidl::android::hardware::neuralnetworks::utils
|
||||||
|
|||||||
254
neuralnetworks/aidl/utils/test/ExecutionTest.cpp
Normal file
254
neuralnetworks/aidl/utils/test/ExecutionTest.cpp
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "MockExecution.h"
|
||||||
|
#include "MockFencedExecutionCallback.h"
|
||||||
|
|
||||||
|
#include <aidl/android/hardware/neuralnetworks/IFencedExecutionCallback.h>
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <nnapi/IExecution.h>
|
||||||
|
#include <nnapi/TypeUtils.h>
|
||||||
|
#include <nnapi/Types.h>
|
||||||
|
#include <nnapi/hal/aidl/Execution.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace aidl::android::hardware::neuralnetworks::utils {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using ::testing::_;
|
||||||
|
using ::testing::DoAll;
|
||||||
|
using ::testing::Invoke;
|
||||||
|
using ::testing::InvokeWithoutArgs;
|
||||||
|
using ::testing::SetArgPointee;
|
||||||
|
|
||||||
|
const std::shared_ptr<IExecution> kInvalidExecution;
|
||||||
|
constexpr auto kNoTiming = Timing{.timeOnDeviceNs = -1, .timeInDriverNs = -1};
|
||||||
|
|
||||||
|
constexpr auto makeStatusOk = [] { return ndk::ScopedAStatus::ok(); };
|
||||||
|
|
||||||
|
constexpr auto makeGeneralFailure = [] {
|
||||||
|
return ndk::ScopedAStatus::fromServiceSpecificError(
|
||||||
|
static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE));
|
||||||
|
};
|
||||||
|
constexpr auto makeGeneralTransportFailure = [] {
|
||||||
|
return ndk::ScopedAStatus::fromStatus(STATUS_NO_MEMORY);
|
||||||
|
};
|
||||||
|
constexpr auto makeDeadObjectFailure = [] {
|
||||||
|
return ndk::ScopedAStatus::fromStatus(STATUS_DEAD_OBJECT);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto makeFencedExecutionResult(const std::shared_ptr<MockFencedExecutionCallback>& callback) {
|
||||||
|
return [callback](const std::vector<ndk::ScopedFileDescriptor>& /*waitFor*/,
|
||||||
|
int64_t /*deadline*/, int64_t /*duration*/,
|
||||||
|
FencedExecutionResult* fencedExecutionResult) {
|
||||||
|
*fencedExecutionResult = FencedExecutionResult{.callback = callback,
|
||||||
|
.syncFence = ndk::ScopedFileDescriptor(-1)};
|
||||||
|
return ndk::ScopedAStatus::ok();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST(ExecutionTest, invalidExecution) {
|
||||||
|
// run test
|
||||||
|
const auto result = Execution::create(kInvalidExecution, {});
|
||||||
|
|
||||||
|
// verify result
|
||||||
|
ASSERT_FALSE(result.has_value());
|
||||||
|
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ExecutionTest, executeSync) {
|
||||||
|
// setup call
|
||||||
|
const auto mockExecution = MockExecution::create();
|
||||||
|
const auto execution = Execution::create(mockExecution, {}).value();
|
||||||
|
const auto mockExecutionResult = ExecutionResult{
|
||||||
|
.outputSufficientSize = true,
|
||||||
|
.outputShapes = {},
|
||||||
|
.timing = kNoTiming,
|
||||||
|
};
|
||||||
|
EXPECT_CALL(*mockExecution, executeSynchronously(_, _))
|
||||||
|
.Times(1)
|
||||||
|
.WillOnce(
|
||||||
|
DoAll(SetArgPointee<1>(mockExecutionResult), InvokeWithoutArgs(makeStatusOk)));
|
||||||
|
|
||||||
|
// run test
|
||||||
|
const auto result = execution->compute({});
|
||||||
|
|
||||||
|
// verify result
|
||||||
|
EXPECT_TRUE(result.has_value())
|
||||||
|
<< "Failed with " << result.error().code << ": " << result.error().message;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ExecutionTest, executeSyncError) {
|
||||||
|
// setup test
|
||||||
|
const auto mockExecution = MockExecution::create();
|
||||||
|
const auto execution = Execution::create(mockExecution, {}).value();
|
||||||
|
EXPECT_CALL(*mockExecution, executeSynchronously(_, _))
|
||||||
|
.Times(1)
|
||||||
|
.WillOnce(Invoke(makeGeneralFailure));
|
||||||
|
|
||||||
|
// run test
|
||||||
|
const auto result = execution->compute({});
|
||||||
|
|
||||||
|
// verify result
|
||||||
|
ASSERT_FALSE(result.has_value());
|
||||||
|
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ExecutionTest, executeSyncTransportFailure) {
|
||||||
|
// setup test
|
||||||
|
const auto mockExecution = MockExecution::create();
|
||||||
|
const auto execution = Execution::create(mockExecution, {}).value();
|
||||||
|
EXPECT_CALL(*mockExecution, executeSynchronously(_, _))
|
||||||
|
.Times(1)
|
||||||
|
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
|
||||||
|
|
||||||
|
// run test
|
||||||
|
const auto result = execution->compute({});
|
||||||
|
|
||||||
|
// verify result
|
||||||
|
ASSERT_FALSE(result.has_value());
|
||||||
|
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ExecutionTest, executeSyncDeadObject) {
|
||||||
|
// setup test
|
||||||
|
const auto mockExecution = MockExecution::create();
|
||||||
|
const auto execution = Execution::create(mockExecution, {}).value();
|
||||||
|
EXPECT_CALL(*mockExecution, executeSynchronously(_, _))
|
||||||
|
.Times(1)
|
||||||
|
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
|
||||||
|
|
||||||
|
// run test
|
||||||
|
const auto result = execution->compute({});
|
||||||
|
|
||||||
|
// verify result
|
||||||
|
ASSERT_FALSE(result.has_value());
|
||||||
|
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ExecutionTest, executeFenced) {
|
||||||
|
// setup call
|
||||||
|
const auto mockExecution = MockExecution::create();
|
||||||
|
const auto execution = Execution::create(mockExecution, {}).value();
|
||||||
|
const auto mockCallback = MockFencedExecutionCallback::create();
|
||||||
|
EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _))
|
||||||
|
.Times(1)
|
||||||
|
.WillOnce(DoAll(SetArgPointee<0>(kNoTiming), SetArgPointee<1>(kNoTiming),
|
||||||
|
SetArgPointee<2>(ErrorStatus::NONE), Invoke(makeStatusOk)));
|
||||||
|
EXPECT_CALL(*mockExecution, executeFenced(_, _, _, _))
|
||||||
|
.Times(1)
|
||||||
|
.WillOnce(Invoke(makeFencedExecutionResult(mockCallback)));
|
||||||
|
|
||||||
|
// run test
|
||||||
|
const auto result = execution->computeFenced({}, {}, {});
|
||||||
|
|
||||||
|
// verify result
|
||||||
|
ASSERT_TRUE(result.has_value())
|
||||||
|
<< "Failed with " << result.error().code << ": " << result.error().message;
|
||||||
|
const auto& [syncFence, callback] = result.value();
|
||||||
|
EXPECT_EQ(syncFence.syncWait({}), nn::SyncFence::FenceState::SIGNALED);
|
||||||
|
ASSERT_NE(callback, nullptr);
|
||||||
|
|
||||||
|
// get results from callback
|
||||||
|
const auto callbackResult = callback();
|
||||||
|
ASSERT_TRUE(callbackResult.has_value()) << "Failed with " << callbackResult.error().code << ": "
|
||||||
|
<< callbackResult.error().message;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ExecutionTest, executeFencedCallbackError) {
|
||||||
|
// setup call
|
||||||
|
const auto mockExecution = MockExecution::create();
|
||||||
|
const auto execution = Execution::create(mockExecution, {}).value();
|
||||||
|
const auto mockCallback = MockFencedExecutionCallback::create();
|
||||||
|
EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _))
|
||||||
|
.Times(1)
|
||||||
|
.WillOnce(Invoke(DoAll(SetArgPointee<0>(kNoTiming), SetArgPointee<1>(kNoTiming),
|
||||||
|
SetArgPointee<2>(ErrorStatus::GENERAL_FAILURE),
|
||||||
|
Invoke(makeStatusOk))));
|
||||||
|
EXPECT_CALL(*mockExecution, executeFenced(_, _, _, _))
|
||||||
|
.Times(1)
|
||||||
|
.WillOnce(Invoke(makeFencedExecutionResult(mockCallback)));
|
||||||
|
|
||||||
|
// run test
|
||||||
|
const auto result = execution->computeFenced({}, {}, {});
|
||||||
|
|
||||||
|
// verify result
|
||||||
|
ASSERT_TRUE(result.has_value())
|
||||||
|
<< "Failed with " << result.error().code << ": " << result.error().message;
|
||||||
|
const auto& [syncFence, callback] = result.value();
|
||||||
|
EXPECT_NE(syncFence.syncWait({}), nn::SyncFence::FenceState::ACTIVE);
|
||||||
|
ASSERT_NE(callback, nullptr);
|
||||||
|
|
||||||
|
// verify callback failure
|
||||||
|
const auto callbackResult = callback();
|
||||||
|
ASSERT_FALSE(callbackResult.has_value());
|
||||||
|
EXPECT_EQ(callbackResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ExecutionTest, executeFencedError) {
|
||||||
|
// setup test
|
||||||
|
const auto mockExecution = MockExecution::create();
|
||||||
|
const auto execution = Execution::create(mockExecution, {}).value();
|
||||||
|
EXPECT_CALL(*mockExecution, executeFenced(_, _, _, _))
|
||||||
|
.Times(1)
|
||||||
|
.WillOnce(InvokeWithoutArgs(makeGeneralFailure));
|
||||||
|
|
||||||
|
// run test
|
||||||
|
const auto result = execution->computeFenced({}, {}, {});
|
||||||
|
|
||||||
|
// verify result
|
||||||
|
ASSERT_FALSE(result.has_value());
|
||||||
|
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ExecutionTest, executeFencedTransportFailure) {
|
||||||
|
// setup test
|
||||||
|
const auto mockExecution = MockExecution::create();
|
||||||
|
const auto execution = Execution::create(mockExecution, {}).value();
|
||||||
|
EXPECT_CALL(*mockExecution, executeFenced(_, _, _, _))
|
||||||
|
.Times(1)
|
||||||
|
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
|
||||||
|
|
||||||
|
// run test
|
||||||
|
const auto result = execution->computeFenced({}, {}, {});
|
||||||
|
|
||||||
|
// verify result
|
||||||
|
ASSERT_FALSE(result.has_value());
|
||||||
|
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ExecutionTest, executeFencedDeadObject) {
|
||||||
|
// setup test
|
||||||
|
const auto mockExecution = MockExecution::create();
|
||||||
|
const auto execution = Execution::create(mockExecution, {}).value();
|
||||||
|
EXPECT_CALL(*mockExecution, executeFenced(_, _, _, _))
|
||||||
|
.Times(1)
|
||||||
|
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
|
||||||
|
|
||||||
|
// run test
|
||||||
|
const auto result = execution->computeFenced({}, {}, {});
|
||||||
|
|
||||||
|
// verify result
|
||||||
|
ASSERT_FALSE(result.has_value());
|
||||||
|
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace aidl::android::hardware::neuralnetworks::utils
|
||||||
47
neuralnetworks/aidl/utils/test/MockExecution.h
Normal file
47
neuralnetworks/aidl/utils/test/MockExecution.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_EXECUTION_H
|
||||||
|
#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_EXECUTION_H
|
||||||
|
|
||||||
|
#include <aidl/android/hardware/neuralnetworks/BnExecution.h>
|
||||||
|
#include <android/binder_interface_utils.h>
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <hidl/HidlSupport.h>
|
||||||
|
#include <hidl/Status.h>
|
||||||
|
|
||||||
|
namespace aidl::android::hardware::neuralnetworks::utils {
|
||||||
|
|
||||||
|
class MockExecution final : public BnExecution {
|
||||||
|
public:
|
||||||
|
static std::shared_ptr<MockExecution> create();
|
||||||
|
|
||||||
|
MOCK_METHOD(ndk::ScopedAStatus, executeSynchronously,
|
||||||
|
(int64_t deadline, ExecutionResult* executionResult), (override));
|
||||||
|
MOCK_METHOD(ndk::ScopedAStatus, executeFenced,
|
||||||
|
(const std::vector<ndk::ScopedFileDescriptor>& waitFor, int64_t deadline,
|
||||||
|
int64_t duration, FencedExecutionResult* fencedExecutionResult),
|
||||||
|
(override));
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::shared_ptr<MockExecution> MockExecution::create() {
|
||||||
|
return ndk::SharedRefBase::make<MockExecution>();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace aidl::android::hardware::neuralnetworks::utils
|
||||||
|
|
||||||
|
#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_EXECUTION_H
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_PREPARED_MODEL_H
|
#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_PREPARED_MODEL_H
|
||||||
#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_PREPARED_MODEL_H
|
#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_PREPARED_MODEL_H
|
||||||
|
|
||||||
|
#include <aidl/android/hardware/neuralnetworks/BnExecution.h>
|
||||||
#include <aidl/android/hardware/neuralnetworks/BnPreparedModel.h>
|
#include <aidl/android/hardware/neuralnetworks/BnPreparedModel.h>
|
||||||
#include <android/binder_interface_utils.h>
|
#include <android/binder_interface_utils.h>
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
@@ -41,6 +42,10 @@ class MockPreparedModel final : public BnPreparedModel {
|
|||||||
(override));
|
(override));
|
||||||
MOCK_METHOD(ndk::ScopedAStatus, configureExecutionBurst, (std::shared_ptr<IBurst> * burst),
|
MOCK_METHOD(ndk::ScopedAStatus, configureExecutionBurst, (std::shared_ptr<IBurst> * burst),
|
||||||
(override));
|
(override));
|
||||||
|
MOCK_METHOD(ndk::ScopedAStatus, createReusableExecution,
|
||||||
|
(const Request& request, bool measureTiming, int64_t loopTimeoutDuration,
|
||||||
|
std::shared_ptr<IExecution>* execution),
|
||||||
|
(override));
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::shared_ptr<MockPreparedModel> MockPreparedModel::create() {
|
inline std::shared_ptr<MockPreparedModel> MockPreparedModel::create() {
|
||||||
|
|||||||
@@ -15,8 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "MockBurst.h"
|
#include "MockBurst.h"
|
||||||
|
#include "MockExecution.h"
|
||||||
#include "MockFencedExecutionCallback.h"
|
#include "MockFencedExecutionCallback.h"
|
||||||
#include "MockPreparedModel.h"
|
#include "MockPreparedModel.h"
|
||||||
|
#include "TestUtils.h"
|
||||||
|
|
||||||
#include <aidl/android/hardware/neuralnetworks/IFencedExecutionCallback.h>
|
#include <aidl/android/hardware/neuralnetworks/IFencedExecutionCallback.h>
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
@@ -66,21 +68,23 @@ auto makeFencedExecutionResult(const std::shared_ptr<MockFencedExecutionCallback
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PreparedModelTest : public VersionedAidlUtilsTestBase {};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST(PreparedModelTest, invalidPreparedModel) {
|
TEST_P(PreparedModelTest, invalidPreparedModel) {
|
||||||
// run test
|
// run test
|
||||||
const auto result = PreparedModel::create(kInvalidPreparedModel);
|
const auto result = PreparedModel::create(kInvalidPreparedModel, kVersion);
|
||||||
|
|
||||||
// verify result
|
// verify result
|
||||||
ASSERT_FALSE(result.has_value());
|
ASSERT_FALSE(result.has_value());
|
||||||
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, executeSync) {
|
TEST_P(PreparedModelTest, executeSync) {
|
||||||
// setup call
|
// setup call
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
const auto mockExecutionResult = ExecutionResult{
|
const auto mockExecutionResult = ExecutionResult{
|
||||||
.outputSufficientSize = true,
|
.outputSufficientSize = true,
|
||||||
.outputShapes = {},
|
.outputShapes = {},
|
||||||
@@ -99,10 +103,10 @@ TEST(PreparedModelTest, executeSync) {
|
|||||||
<< "Failed with " << result.error().code << ": " << result.error().message;
|
<< "Failed with " << result.error().code << ": " << result.error().message;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, executeSyncError) {
|
TEST_P(PreparedModelTest, executeSyncError) {
|
||||||
// setup test
|
// setup test
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
|
EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
.WillOnce(Invoke(makeGeneralFailure));
|
.WillOnce(Invoke(makeGeneralFailure));
|
||||||
@@ -115,10 +119,10 @@ TEST(PreparedModelTest, executeSyncError) {
|
|||||||
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, executeSyncTransportFailure) {
|
TEST_P(PreparedModelTest, executeSyncTransportFailure) {
|
||||||
// setup test
|
// setup test
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
|
EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
|
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
|
||||||
@@ -131,10 +135,10 @@ TEST(PreparedModelTest, executeSyncTransportFailure) {
|
|||||||
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, executeSyncDeadObject) {
|
TEST_P(PreparedModelTest, executeSyncDeadObject) {
|
||||||
// setup test
|
// setup test
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
|
EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
|
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
|
||||||
@@ -147,10 +151,10 @@ TEST(PreparedModelTest, executeSyncDeadObject) {
|
|||||||
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
|
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, executeFenced) {
|
TEST_P(PreparedModelTest, executeFenced) {
|
||||||
// setup call
|
// setup call
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
const auto mockCallback = MockFencedExecutionCallback::create();
|
const auto mockCallback = MockFencedExecutionCallback::create();
|
||||||
EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _))
|
EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
@@ -176,10 +180,10 @@ TEST(PreparedModelTest, executeFenced) {
|
|||||||
<< callbackResult.error().message;
|
<< callbackResult.error().message;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, executeFencedCallbackError) {
|
TEST_P(PreparedModelTest, executeFencedCallbackError) {
|
||||||
// setup call
|
// setup call
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
const auto mockCallback = MockFencedExecutionCallback::create();
|
const auto mockCallback = MockFencedExecutionCallback::create();
|
||||||
EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _))
|
EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
@@ -206,10 +210,10 @@ TEST(PreparedModelTest, executeFencedCallbackError) {
|
|||||||
EXPECT_EQ(callbackResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
EXPECT_EQ(callbackResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, executeFencedError) {
|
TEST_P(PreparedModelTest, executeFencedError) {
|
||||||
// setup test
|
// setup test
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
|
EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
.WillOnce(InvokeWithoutArgs(makeGeneralFailure));
|
.WillOnce(InvokeWithoutArgs(makeGeneralFailure));
|
||||||
@@ -222,10 +226,10 @@ TEST(PreparedModelTest, executeFencedError) {
|
|||||||
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, executeFencedTransportFailure) {
|
TEST_P(PreparedModelTest, executeFencedTransportFailure) {
|
||||||
// setup test
|
// setup test
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
|
EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
|
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
|
||||||
@@ -238,10 +242,10 @@ TEST(PreparedModelTest, executeFencedTransportFailure) {
|
|||||||
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, executeFencedDeadObject) {
|
TEST_P(PreparedModelTest, executeFencedDeadObject) {
|
||||||
// setup test
|
// setup test
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
|
EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
|
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
|
||||||
@@ -254,11 +258,13 @@ TEST(PreparedModelTest, executeFencedDeadObject) {
|
|||||||
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
|
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, reusableExecuteSync) {
|
TEST_P(PreparedModelTest, reusableExecuteSync) {
|
||||||
|
if (kVersion.level >= nn::Version::Level::FEATURE_LEVEL_8) return;
|
||||||
|
|
||||||
// setup call
|
// setup call
|
||||||
const uint32_t kNumberOfComputations = 2;
|
const uint32_t kNumberOfComputations = 2;
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
const auto mockExecutionResult = ExecutionResult{
|
const auto mockExecutionResult = ExecutionResult{
|
||||||
.outputSufficientSize = true,
|
.outputSufficientSize = true,
|
||||||
.outputShapes = {},
|
.outputShapes = {},
|
||||||
@@ -283,10 +289,12 @@ TEST(PreparedModelTest, reusableExecuteSync) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, reusableExecuteSyncError) {
|
TEST_P(PreparedModelTest, reusableExecuteSyncError) {
|
||||||
|
if (kVersion.level >= nn::Version::Level::FEATURE_LEVEL_8) return;
|
||||||
|
|
||||||
// setup test
|
// setup test
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
|
EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
.WillOnce(Invoke(makeGeneralFailure));
|
.WillOnce(Invoke(makeGeneralFailure));
|
||||||
@@ -303,10 +311,12 @@ TEST(PreparedModelTest, reusableExecuteSyncError) {
|
|||||||
EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, reusableExecuteSyncTransportFailure) {
|
TEST_P(PreparedModelTest, reusableExecuteSyncTransportFailure) {
|
||||||
|
if (kVersion.level >= nn::Version::Level::FEATURE_LEVEL_8) return;
|
||||||
|
|
||||||
// setup test
|
// setup test
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
|
EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
|
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
|
||||||
@@ -323,10 +333,12 @@ TEST(PreparedModelTest, reusableExecuteSyncTransportFailure) {
|
|||||||
EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, reusableExecuteSyncDeadObject) {
|
TEST_P(PreparedModelTest, reusableExecuteSyncDeadObject) {
|
||||||
|
if (kVersion.level >= nn::Version::Level::FEATURE_LEVEL_8) return;
|
||||||
|
|
||||||
// setup test
|
// setup test
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
|
EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
|
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
|
||||||
@@ -343,11 +355,13 @@ TEST(PreparedModelTest, reusableExecuteSyncDeadObject) {
|
|||||||
EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
|
EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, reusableExecuteFenced) {
|
TEST_P(PreparedModelTest, reusableExecuteFenced) {
|
||||||
|
if (kVersion.level >= nn::Version::Level::FEATURE_LEVEL_8) return;
|
||||||
|
|
||||||
// setup call
|
// setup call
|
||||||
const uint32_t kNumberOfComputations = 2;
|
const uint32_t kNumberOfComputations = 2;
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
const auto mockCallback = MockFencedExecutionCallback::create();
|
const auto mockCallback = MockFencedExecutionCallback::create();
|
||||||
EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _))
|
EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _))
|
||||||
.Times(kNumberOfComputations)
|
.Times(kNumberOfComputations)
|
||||||
@@ -379,10 +393,12 @@ TEST(PreparedModelTest, reusableExecuteFenced) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, reusableExecuteFencedCallbackError) {
|
TEST_P(PreparedModelTest, reusableExecuteFencedCallbackError) {
|
||||||
|
if (kVersion.level >= nn::Version::Level::FEATURE_LEVEL_8) return;
|
||||||
|
|
||||||
// setup call
|
// setup call
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
const auto mockCallback = MockFencedExecutionCallback::create();
|
const auto mockCallback = MockFencedExecutionCallback::create();
|
||||||
EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _))
|
EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
@@ -413,10 +429,12 @@ TEST(PreparedModelTest, reusableExecuteFencedCallbackError) {
|
|||||||
EXPECT_EQ(callbackResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
EXPECT_EQ(callbackResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, reusableExecuteFencedError) {
|
TEST_P(PreparedModelTest, reusableExecuteFencedError) {
|
||||||
|
if (kVersion.level >= nn::Version::Level::FEATURE_LEVEL_8) return;
|
||||||
|
|
||||||
// setup test
|
// setup test
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
|
EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
.WillOnce(InvokeWithoutArgs(makeGeneralFailure));
|
.WillOnce(InvokeWithoutArgs(makeGeneralFailure));
|
||||||
@@ -433,10 +451,12 @@ TEST(PreparedModelTest, reusableExecuteFencedError) {
|
|||||||
EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, reusableExecuteFencedTransportFailure) {
|
TEST_P(PreparedModelTest, reusableExecuteFencedTransportFailure) {
|
||||||
|
if (kVersion.level >= nn::Version::Level::FEATURE_LEVEL_8) return;
|
||||||
|
|
||||||
// setup test
|
// setup test
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
|
EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
|
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
|
||||||
@@ -453,10 +473,12 @@ TEST(PreparedModelTest, reusableExecuteFencedTransportFailure) {
|
|||||||
EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, reusableExecuteFencedDeadObject) {
|
TEST_P(PreparedModelTest, reusableExecuteFencedDeadObject) {
|
||||||
|
if (kVersion.level >= nn::Version::Level::FEATURE_LEVEL_8) return;
|
||||||
|
|
||||||
// setup test
|
// setup test
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
|
EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
|
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
|
||||||
@@ -473,14 +495,14 @@ TEST(PreparedModelTest, reusableExecuteFencedDeadObject) {
|
|||||||
EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
|
EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, configureExecutionBurst) {
|
TEST_P(PreparedModelTest, configureExecutionBurst) {
|
||||||
// setup test
|
// setup test
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
const auto mockBurst = ndk::SharedRefBase::make<MockBurst>();
|
const auto mockBurst = ndk::SharedRefBase::make<MockBurst>();
|
||||||
EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_))
|
EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
.WillOnce(DoAll(SetArgPointee<0>(mockBurst), Invoke(makeStatusOk)));
|
.WillOnce(DoAll(SetArgPointee<0>(mockBurst), Invoke(makeStatusOk)));
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
|
|
||||||
// run test
|
// run test
|
||||||
const auto result = preparedModel->configureExecutionBurst();
|
const auto result = preparedModel->configureExecutionBurst();
|
||||||
@@ -491,13 +513,13 @@ TEST(PreparedModelTest, configureExecutionBurst) {
|
|||||||
EXPECT_NE(result.value(), nullptr);
|
EXPECT_NE(result.value(), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, configureExecutionBurstError) {
|
TEST_P(PreparedModelTest, configureExecutionBurstError) {
|
||||||
// setup test
|
// setup test
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_))
|
EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
.WillOnce(InvokeWithoutArgs(makeGeneralFailure));
|
.WillOnce(InvokeWithoutArgs(makeGeneralFailure));
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
|
|
||||||
// run test
|
// run test
|
||||||
const auto result = preparedModel->configureExecutionBurst();
|
const auto result = preparedModel->configureExecutionBurst();
|
||||||
@@ -507,13 +529,13 @@ TEST(PreparedModelTest, configureExecutionBurstError) {
|
|||||||
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, configureExecutionBurstTransportFailure) {
|
TEST_P(PreparedModelTest, configureExecutionBurstTransportFailure) {
|
||||||
// setup test
|
// setup test
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_))
|
EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
|
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
|
|
||||||
// run test
|
// run test
|
||||||
const auto result = preparedModel->configureExecutionBurst();
|
const auto result = preparedModel->configureExecutionBurst();
|
||||||
@@ -523,13 +545,13 @@ TEST(PreparedModelTest, configureExecutionBurstTransportFailure) {
|
|||||||
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, configureExecutionBurstDeadObject) {
|
TEST_P(PreparedModelTest, configureExecutionBurstDeadObject) {
|
||||||
// setup test
|
// setup test
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_))
|
EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
|
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
|
|
||||||
// run test
|
// run test
|
||||||
const auto result = preparedModel->configureExecutionBurst();
|
const auto result = preparedModel->configureExecutionBurst();
|
||||||
@@ -539,10 +561,84 @@ TEST(PreparedModelTest, configureExecutionBurstDeadObject) {
|
|||||||
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
|
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PreparedModelTest, getUnderlyingResource) {
|
TEST_P(PreparedModelTest, createReusableExecution) {
|
||||||
|
if (kVersion.level < nn::Version::Level::FEATURE_LEVEL_8) return;
|
||||||
|
|
||||||
// setup test
|
// setup test
|
||||||
const auto mockPreparedModel = MockPreparedModel::create();
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
|
const auto mockExecution = ndk::SharedRefBase::make<MockExecution>();
|
||||||
|
EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _, _))
|
||||||
|
.Times(1)
|
||||||
|
.WillOnce(DoAll(SetArgPointee<3>(mockExecution), Invoke(makeStatusOk)));
|
||||||
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
|
|
||||||
|
// run test
|
||||||
|
const auto result = preparedModel->createReusableExecution({}, {}, {});
|
||||||
|
|
||||||
|
// verify result
|
||||||
|
ASSERT_TRUE(result.has_value())
|
||||||
|
<< "Failed with " << result.error().code << ": " << result.error().message;
|
||||||
|
EXPECT_NE(result.value(), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(PreparedModelTest, createReusableExecutionError) {
|
||||||
|
if (kVersion.level < nn::Version::Level::FEATURE_LEVEL_8) return;
|
||||||
|
|
||||||
|
// setup test
|
||||||
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
|
EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _, _))
|
||||||
|
.Times(1)
|
||||||
|
.WillOnce(InvokeWithoutArgs(makeGeneralFailure));
|
||||||
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
|
|
||||||
|
// run test
|
||||||
|
const auto result = preparedModel->createReusableExecution({}, {}, {});
|
||||||
|
|
||||||
|
// verify result
|
||||||
|
ASSERT_FALSE(result.has_value());
|
||||||
|
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(PreparedModelTest, createReusableExecutionTransportFailure) {
|
||||||
|
if (kVersion.level < nn::Version::Level::FEATURE_LEVEL_8) return;
|
||||||
|
|
||||||
|
// setup test
|
||||||
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
|
EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _, _))
|
||||||
|
.Times(1)
|
||||||
|
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
|
||||||
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
|
|
||||||
|
// run test
|
||||||
|
const auto result = preparedModel->createReusableExecution({}, {}, {});
|
||||||
|
|
||||||
|
// verify result
|
||||||
|
ASSERT_FALSE(result.has_value());
|
||||||
|
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(PreparedModelTest, createReusableExecutionDeadObject) {
|
||||||
|
if (kVersion.level < nn::Version::Level::FEATURE_LEVEL_8) return;
|
||||||
|
|
||||||
|
// setup test
|
||||||
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
|
EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _, _))
|
||||||
|
.Times(1)
|
||||||
|
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
|
||||||
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
|
|
||||||
|
// run test
|
||||||
|
const auto result = preparedModel->createReusableExecution({}, {}, {});
|
||||||
|
|
||||||
|
// verify result
|
||||||
|
ASSERT_FALSE(result.has_value());
|
||||||
|
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(PreparedModelTest, getUnderlyingResource) {
|
||||||
|
// setup test
|
||||||
|
const auto mockPreparedModel = MockPreparedModel::create();
|
||||||
|
const auto preparedModel = PreparedModel::create(mockPreparedModel, kVersion).value();
|
||||||
|
|
||||||
// run test
|
// run test
|
||||||
const auto resource = preparedModel->getUnderlyingResource();
|
const auto resource = preparedModel->getUnderlyingResource();
|
||||||
@@ -554,4 +650,6 @@ TEST(PreparedModelTest, getUnderlyingResource) {
|
|||||||
EXPECT_EQ(maybeMock->get(), mockPreparedModel.get());
|
EXPECT_EQ(maybeMock->get(), mockPreparedModel.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_VERSIONED_AIDL_UTILS_TEST(PreparedModelTest, kAllAidlVersions);
|
||||||
|
|
||||||
} // namespace aidl::android::hardware::neuralnetworks::utils
|
} // namespace aidl::android::hardware::neuralnetworks::utils
|
||||||
|
|||||||
44
neuralnetworks/aidl/utils/test/TestUtils.cpp
Normal file
44
neuralnetworks/aidl/utils/test/TestUtils.cpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "TestUtils.h"
|
||||||
|
|
||||||
|
#include <android-base/logging.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <nnapi/TypeUtils.h>
|
||||||
|
#include <nnapi/Types.h>
|
||||||
|
#include <nnapi/hal/CommonUtils.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace aidl::android::hardware::neuralnetworks::utils {
|
||||||
|
|
||||||
|
std::string printTestVersion(const testing::TestParamInfo<nn::Version>& info) {
|
||||||
|
switch (info.param.level) {
|
||||||
|
case nn::Version::Level::FEATURE_LEVEL_5:
|
||||||
|
return "v1";
|
||||||
|
case nn::Version::Level::FEATURE_LEVEL_6:
|
||||||
|
return "v2";
|
||||||
|
case nn::Version::Level::FEATURE_LEVEL_7:
|
||||||
|
return "v3";
|
||||||
|
case nn::Version::Level::FEATURE_LEVEL_8:
|
||||||
|
return "v4";
|
||||||
|
default:
|
||||||
|
LOG(FATAL) << "Invalid AIDL version: " << info.param;
|
||||||
|
return "invalid";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace aidl::android::hardware::neuralnetworks::utils
|
||||||
43
neuralnetworks/aidl/utils/test/TestUtils.h
Normal file
43
neuralnetworks/aidl/utils/test/TestUtils.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_TEST_UTILS_H
|
||||||
|
#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_TEST_UTILS_H
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <nnapi/Types.h>
|
||||||
|
#include <nnapi/hal/CommonUtils.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace aidl::android::hardware::neuralnetworks::utils {
|
||||||
|
|
||||||
|
class VersionedAidlUtilsTestBase : public ::testing::TestWithParam<nn::Version> {
|
||||||
|
protected:
|
||||||
|
const nn::Version kVersion = GetParam();
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string printTestVersion(const testing::TestParamInfo<nn::Version>& info);
|
||||||
|
|
||||||
|
inline const auto kAllAidlVersions =
|
||||||
|
::testing::Values(nn::kVersionFeatureLevel5, nn::kVersionFeatureLevel6,
|
||||||
|
nn::kVersionFeatureLevel7, nn::kVersionFeatureLevel8);
|
||||||
|
|
||||||
|
#define INSTANTIATE_VERSIONED_AIDL_UTILS_TEST(TestSuite, versions) \
|
||||||
|
INSTANTIATE_TEST_SUITE_P(Versioned, TestSuite, versions, printTestVersion)
|
||||||
|
|
||||||
|
} // namespace aidl::android::hardware::neuralnetworks::utils
|
||||||
|
|
||||||
|
#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_TEST_UTILS_H
|
||||||
@@ -208,6 +208,11 @@ class InvalidPreparedModel : public BnPreparedModel {
|
|||||||
return ndk::ScopedAStatus::fromServiceSpecificError(
|
return ndk::ScopedAStatus::fromServiceSpecificError(
|
||||||
static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE));
|
static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE));
|
||||||
}
|
}
|
||||||
|
ndk::ScopedAStatus createReusableExecution(const aidl_hal::Request&, bool, int64_t,
|
||||||
|
std::shared_ptr<aidl_hal::IExecution>*) override {
|
||||||
|
return ndk::ScopedAStatus::fromServiceSpecificError(
|
||||||
|
static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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_AIDL_EXECUTION_H
|
||||||
|
#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_AIDL_EXECUTION_H
|
||||||
|
|
||||||
|
#include "nnapi/hal/aidl/Adapter.h"
|
||||||
|
|
||||||
|
#include <aidl/android/hardware/neuralnetworks/BnExecution.h>
|
||||||
|
#include <aidl/android/hardware/neuralnetworks/ExecutionResult.h>
|
||||||
|
#include <aidl/android/hardware/neuralnetworks/FencedExecutionResult.h>
|
||||||
|
#include <aidl/android/hardware/neuralnetworks/IExecution.h>
|
||||||
|
#include <aidl/android/hardware/neuralnetworks/Request.h>
|
||||||
|
#include <android/binder_auto_utils.h>
|
||||||
|
#include <nnapi/IExecution.h>
|
||||||
|
#include <nnapi/Types.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// See hardware/interfaces/neuralnetworks/utils/README.md for more information on AIDL interface
|
||||||
|
// lifetimes across processes and for protecting asynchronous calls across AIDL.
|
||||||
|
|
||||||
|
namespace aidl::android::hardware::neuralnetworks::adapter {
|
||||||
|
|
||||||
|
// Class that adapts nn::IExecution to BnExecution.
|
||||||
|
class Execution : public BnExecution {
|
||||||
|
public:
|
||||||
|
explicit Execution(::android::nn::SharedExecution execution);
|
||||||
|
|
||||||
|
ndk::ScopedAStatus executeSynchronously(int64_t deadlineNs,
|
||||||
|
ExecutionResult* executionResult) override;
|
||||||
|
ndk::ScopedAStatus executeFenced(const std::vector<ndk::ScopedFileDescriptor>& waitFor,
|
||||||
|
int64_t deadlineNs, int64_t durationNs,
|
||||||
|
FencedExecutionResult* fencedExecutionResult) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const ::android::nn::SharedExecution kExecution;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace aidl::android::hardware::neuralnetworks::adapter
|
||||||
|
|
||||||
|
#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_AIDL_EXECUTION_H
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <aidl/android/hardware/neuralnetworks/ExecutionResult.h>
|
#include <aidl/android/hardware/neuralnetworks/ExecutionResult.h>
|
||||||
#include <aidl/android/hardware/neuralnetworks/FencedExecutionResult.h>
|
#include <aidl/android/hardware/neuralnetworks/FencedExecutionResult.h>
|
||||||
#include <aidl/android/hardware/neuralnetworks/IBurst.h>
|
#include <aidl/android/hardware/neuralnetworks/IBurst.h>
|
||||||
|
#include <aidl/android/hardware/neuralnetworks/IExecution.h>
|
||||||
#include <aidl/android/hardware/neuralnetworks/Request.h>
|
#include <aidl/android/hardware/neuralnetworks/Request.h>
|
||||||
#include <android/binder_auto_utils.h>
|
#include <android/binder_auto_utils.h>
|
||||||
#include <nnapi/IPreparedModel.h>
|
#include <nnapi/IPreparedModel.h>
|
||||||
@@ -50,6 +51,9 @@ class PreparedModel : public BnPreparedModel {
|
|||||||
int64_t loopTimeoutDurationNs, int64_t durationNs,
|
int64_t loopTimeoutDurationNs, int64_t durationNs,
|
||||||
FencedExecutionResult* executionResult) override;
|
FencedExecutionResult* executionResult) override;
|
||||||
ndk::ScopedAStatus configureExecutionBurst(std::shared_ptr<IBurst>* burst) override;
|
ndk::ScopedAStatus configureExecutionBurst(std::shared_ptr<IBurst>* burst) override;
|
||||||
|
ndk::ScopedAStatus createReusableExecution(const Request& request, bool measureTiming,
|
||||||
|
int64_t loopTimeoutDurationNs,
|
||||||
|
std::shared_ptr<IExecution>* execution) override;
|
||||||
|
|
||||||
::android::nn::SharedPreparedModel getUnderlyingPreparedModel() const;
|
::android::nn::SharedPreparedModel getUnderlyingPreparedModel() const;
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "PreparedModel.h"
|
#include "PreparedModel.h"
|
||||||
|
|
||||||
#include "Burst.h"
|
#include "Burst.h"
|
||||||
|
#include "Execution.h"
|
||||||
|
|
||||||
#include <aidl/android/hardware/neuralnetworks/BnFencedExecutionCallback.h>
|
#include <aidl/android/hardware/neuralnetworks/BnFencedExecutionCallback.h>
|
||||||
#include <aidl/android/hardware/neuralnetworks/BnPreparedModel.h>
|
#include <aidl/android/hardware/neuralnetworks/BnPreparedModel.h>
|
||||||
@@ -26,6 +27,7 @@
|
|||||||
#include <aidl/android/hardware/neuralnetworks/Request.h>
|
#include <aidl/android/hardware/neuralnetworks/Request.h>
|
||||||
#include <android-base/logging.h>
|
#include <android-base/logging.h>
|
||||||
#include <android/binder_auto_utils.h>
|
#include <android/binder_auto_utils.h>
|
||||||
|
#include <nnapi/IExecution.h>
|
||||||
#include <nnapi/IPreparedModel.h>
|
#include <nnapi/IPreparedModel.h>
|
||||||
#include <nnapi/Result.h>
|
#include <nnapi/Result.h>
|
||||||
#include <nnapi/SharedMemory.h>
|
#include <nnapi/SharedMemory.h>
|
||||||
@@ -167,6 +169,56 @@ nn::GeneralResult<FencedExecutionResult> executeFenced(
|
|||||||
.syncFence = std::move(fileDescriptor)};
|
.syncFence = std::move(fileDescriptor)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nn::GeneralResult<nn::SharedExecution> createReusableExecution(
|
||||||
|
const nn::IPreparedModel& preparedModel, const Request& request, bool measureTiming,
|
||||||
|
int64_t loopTimeoutDurationNs) {
|
||||||
|
const auto nnRequest = NN_TRY(convertInput(request));
|
||||||
|
const auto nnMeasureTiming = measureTiming ? nn::MeasureTiming::YES : nn::MeasureTiming::NO;
|
||||||
|
const auto nnLoopTimeoutDuration = NN_TRY(makeOptionalDuration(loopTimeoutDurationNs));
|
||||||
|
return preparedModel.createReusableExecution(nnRequest, nnMeasureTiming, nnLoopTimeoutDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
nn::ExecutionResult<ExecutionResult> executeSynchronously(const nn::IExecution& execution,
|
||||||
|
int64_t deadlineNs) {
|
||||||
|
const auto nnDeadline = NN_TRY(makeOptionalTimePoint(deadlineNs));
|
||||||
|
|
||||||
|
const auto result = execution.compute(nnDeadline);
|
||||||
|
|
||||||
|
if (!result.ok() && result.error().code == nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
|
||||||
|
const auto& [message, code, outputShapes] = result.error();
|
||||||
|
LOG(ERROR) << "executeSynchronously failed with " << code << ": " << message;
|
||||||
|
return ExecutionResult{.outputSufficientSize = false,
|
||||||
|
.outputShapes = utils::convert(outputShapes).value(),
|
||||||
|
.timing = {.timeInDriverNs = -1, .timeOnDeviceNs = -1}};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& [outputShapes, timing] = NN_TRY(result);
|
||||||
|
return ExecutionResult{.outputSufficientSize = true,
|
||||||
|
.outputShapes = utils::convert(outputShapes).value(),
|
||||||
|
.timing = utils::convert(timing).value()};
|
||||||
|
}
|
||||||
|
|
||||||
|
nn::GeneralResult<FencedExecutionResult> executeFenced(
|
||||||
|
const nn::IExecution& execution, const std::vector<ndk::ScopedFileDescriptor>& waitFor,
|
||||||
|
int64_t deadlineNs, int64_t durationNs) {
|
||||||
|
const auto nnWaitFor = NN_TRY(convertSyncFences(waitFor));
|
||||||
|
const auto nnDeadline = NN_TRY(makeOptionalTimePoint(deadlineNs));
|
||||||
|
const auto nnDuration = NN_TRY(makeOptionalDuration(durationNs));
|
||||||
|
|
||||||
|
auto [syncFence, executeFencedInfoCallback] =
|
||||||
|
NN_TRY(execution.computeFenced(nnWaitFor, nnDeadline, nnDuration));
|
||||||
|
|
||||||
|
ndk::ScopedFileDescriptor fileDescriptor;
|
||||||
|
if (syncFence.hasFd()) {
|
||||||
|
auto uniqueFd = NN_TRY(nn::dupFd(syncFence.getFd()));
|
||||||
|
fileDescriptor = ndk::ScopedFileDescriptor(uniqueFd.release());
|
||||||
|
}
|
||||||
|
|
||||||
|
return FencedExecutionResult{.callback = ndk::SharedRefBase::make<FencedExecutionCallback>(
|
||||||
|
std::move(executeFencedInfoCallback)),
|
||||||
|
.syncFence = std::move(fileDescriptor)};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
PreparedModel::PreparedModel(nn::SharedPreparedModel preparedModel)
|
PreparedModel::PreparedModel(nn::SharedPreparedModel preparedModel)
|
||||||
@@ -222,4 +274,51 @@ nn::SharedPreparedModel PreparedModel::getUnderlyingPreparedModel() const {
|
|||||||
return kPreparedModel;
|
return kPreparedModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ndk::ScopedAStatus PreparedModel::createReusableExecution(const Request& request,
|
||||||
|
bool measureTiming,
|
||||||
|
int64_t loopTimeoutDurationNs,
|
||||||
|
std::shared_ptr<IExecution>* execution) {
|
||||||
|
auto result = adapter::createReusableExecution(*kPreparedModel, request, measureTiming,
|
||||||
|
loopTimeoutDurationNs);
|
||||||
|
if (!result.has_value()) {
|
||||||
|
const auto& [message, code] = result.error();
|
||||||
|
const auto aidlCode = utils::convert(code).value_or(ErrorStatus::GENERAL_FAILURE);
|
||||||
|
return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
|
||||||
|
static_cast<int32_t>(aidlCode), message.c_str());
|
||||||
|
}
|
||||||
|
*execution = ndk::SharedRefBase::make<Execution>(std::move(result).value());
|
||||||
|
return ndk::ScopedAStatus::ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
Execution::Execution(nn::SharedExecution execution) : kExecution(std::move(execution)) {
|
||||||
|
CHECK(kExecution != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
ndk::ScopedAStatus Execution::executeSynchronously(int64_t deadlineNs,
|
||||||
|
ExecutionResult* executionResult) {
|
||||||
|
auto result = adapter::executeSynchronously(*kExecution, deadlineNs);
|
||||||
|
if (!result.has_value()) {
|
||||||
|
const auto& [message, code, _] = result.error();
|
||||||
|
const auto aidlCode = utils::convert(code).value_or(ErrorStatus::GENERAL_FAILURE);
|
||||||
|
return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
|
||||||
|
static_cast<int32_t>(aidlCode), message.c_str());
|
||||||
|
}
|
||||||
|
*executionResult = std::move(result).value();
|
||||||
|
return ndk::ScopedAStatus::ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
ndk::ScopedAStatus Execution::executeFenced(const std::vector<ndk::ScopedFileDescriptor>& waitFor,
|
||||||
|
int64_t deadlineNs, int64_t durationNs,
|
||||||
|
FencedExecutionResult* executionResult) {
|
||||||
|
auto result = adapter::executeFenced(*kExecution, waitFor, deadlineNs, durationNs);
|
||||||
|
if (!result.has_value()) {
|
||||||
|
const auto& [message, code] = result.error();
|
||||||
|
const auto aidlCode = utils::convert(code).value_or(ErrorStatus::GENERAL_FAILURE);
|
||||||
|
return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
|
||||||
|
static_cast<int32_t>(aidlCode), message.c_str());
|
||||||
|
}
|
||||||
|
*executionResult = std::move(result).value();
|
||||||
|
return ndk::ScopedAStatus::ok();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace aidl::android::hardware::neuralnetworks::adapter
|
} // namespace aidl::android::hardware::neuralnetworks::adapter
|
||||||
|
|||||||
Reference in New Issue
Block a user