From e4f1583337f5b134b80705626c9a21ee2a533472 Mon Sep 17 00:00:00 2001 From: Lev Proleev Date: Mon, 20 Jan 2020 13:39:15 +0000 Subject: [PATCH 1/6] NNAPI: Regenerate types.hal to sync with types.spec Bug: 147765446 Test: mma Change-Id: I2b41bd959b7084ecb89674c659c66ebb158ac77d Merged-In: I2b41bd959b7084ecb89674c659c66ebb158ac77d (cherry picked from commit 2d7c489f6779698069bf0623fe11d29784ea96d4) --- current.txt | 2 +- neuralnetworks/1.3/types.hal | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/current.txt b/current.txt index e737314527..f0e7bb40dd 100644 --- a/current.txt +++ b/current.txt @@ -627,7 +627,7 @@ d1f382d14e1384b907d5bb5780df7f01934650d556fedbed2f15a90773c657d6 android.hardwar 4167dc3ad35e9cd0d2057d4868c7675ae2c3c9d05bbd614c1f5dccfa5fd68797 android.hardware.neuralnetworks@1.3::IExecutionCallback 7d23020248194abbee8091cc624f39a5a6d7ccba338b172d5d2d3df0cceffbee android.hardware.neuralnetworks@1.3::IPreparedModel 0439a1fbbec7f16e5e4c653d85ac685d51bfafbae15b8f8cca530acdd7d6a8ce android.hardware.neuralnetworks@1.3::IPreparedModelCallback -ee65638f8af3f9f4f222e7208eaa9f1f8e7f8e0a21545846ba67d0e27624efa1 android.hardware.neuralnetworks@1.3::types +162515505235bc770601f02c3537f9ccf11582583bf7b11dd2ec81fab6855333 android.hardware.neuralnetworks@1.3::types 3e01d4446cd69fd1c48f8572efd97487bc179564b32bd795800b97bbe10be37b android.hardware.wifi@1.4::IWifi a64467bae843569f0d465c5be7f0c7a5b987985b55a3ef4794dd5afc68538650 android.hardware.wifi.supplicant@1.3::ISupplicant 44445b8a03d7b9e68b2fbd954672c18a8fce9e32851b0692f4f4ab3407f86ecb android.hardware.wifi.supplicant@1.3::ISupplicantStaIface diff --git a/neuralnetworks/1.3/types.hal b/neuralnetworks/1.3/types.hal index b330b50084..0f51b1a048 100644 --- a/neuralnetworks/1.3/types.hal +++ b/neuralnetworks/1.3/types.hal @@ -1415,6 +1415,7 @@ enum OperationType : int32_t { * * {@link OperandType::TENSOR_FLOAT32} * * {@link OperandType::TENSOR_QUANT8_ASYMM} * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3) + * * {@link OperandType::TENSOR_INT32} (since HAL version 1.3) * * Supported tensor rank: up to 4 * @@ -1425,6 +1426,8 @@ enum OperationType : int32_t { * * 2: An {@link OperandType::INT32} scalar, and has to be one of the * {@link FusedActivationFunc} values. Specifies the activation to * invoke on the result. + * For a {@link OperandType::TENSOR_INT32} tensor, + * the {@link FusedActivationFunc} must be "NONE". * * Outputs: * * 0: The product, a tensor of the same {@link OperandType} as input0. @@ -1905,6 +1908,11 @@ enum OperationType : int32_t { * dimensions. The output is the result of dividing the first input tensor * by the second, optionally modified by an activation function. * + * For inputs of {@link OperandType::TENSOR_INT32}, performs + * "floor division" ("//" in Python). For example, + * 5 // 2 = 2 + * -5 // 2 = -3 + * * Two dimensions are compatible when: * 1. they are equal, or * 2. one of them is 1 @@ -1925,6 +1933,7 @@ enum OperationType : int32_t { * Supported tensor {@link OperandType}: * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2) * * {@link OperandType::TENSOR_FLOAT32} + * * {@link OperandType::TENSOR_INT32} (since HAL version 1.3) * * Supported tensor rank: up to 4 * @@ -1935,6 +1944,8 @@ enum OperationType : int32_t { * * 2: An {@link OperandType::INT32} scalar, and has to be one of the * {@link FusedActivationFunc} values. Specifies the activation to * invoke on the result. + * For a {@link OperandType::TENSOR_INT32} tensor, + * the {@link FusedActivationFunc} must be "NONE". * * Outputs: * * 0: A tensor of the same {@link OperandType} as input0. @@ -2186,6 +2197,7 @@ enum OperationType : int32_t { * * {@link OperandType::TENSOR_FLOAT32} * * {@link OperandType::TENSOR_QUANT8_ASYMM} (since HAL version 1.2) * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3) + * * {@link OperandType::TENSOR_INT32} (since HAL version 1.3) * * Supported tensor rank: up to 4 * @@ -2196,6 +2208,8 @@ enum OperationType : int32_t { * * 2: An {@link OperandType::INT32} scalar, and has to be one of the * {@link FusedActivationFunc} values. Specifies the activation to * invoke on the result. + * For a {@link OperandType::TENSOR_INT32} tensor, + * the {@link FusedActivationFunc} must be "NONE". * * Outputs: * * 0: A tensor of the same {@link OperandType} as input0. @@ -2242,6 +2256,7 @@ enum OperationType : int32_t { * Supported tensor {@link OperandType}: * * {@link OperandType::TENSOR_FLOAT16} * * {@link OperandType::TENSOR_FLOAT32} + * * {@link OperandType::TENSOR_INT32} (since HAL version 1.3) * * Supported tensor rank: from 1. * From 503d85036929101de2af53dea29c18528d178805 Mon Sep 17 00:00:00 2001 From: Lev Proleev Date: Mon, 6 Jan 2020 12:16:31 +0000 Subject: [PATCH 2/6] Add ELU and HARD_SWISH Bug: 147482068 Bug: 147481241 Test: NNTest_static and VtsHalNeuralnetworksV1_3TargetTest Change-Id: Iab8da2a666ad9775dfb53d9297e94962fb651353 Merged-In: Iab8da2a666ad9775dfb53d9297e94962fb651353 (cherry picked from commit aee67f83f9e97e060ff31e16e6898a15f4680d04) --- current.txt | 2 +- neuralnetworks/1.3/types.hal | 52 +++++++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/current.txt b/current.txt index f0e7bb40dd..d615eaa720 100644 --- a/current.txt +++ b/current.txt @@ -627,7 +627,7 @@ d1f382d14e1384b907d5bb5780df7f01934650d556fedbed2f15a90773c657d6 android.hardwar 4167dc3ad35e9cd0d2057d4868c7675ae2c3c9d05bbd614c1f5dccfa5fd68797 android.hardware.neuralnetworks@1.3::IExecutionCallback 7d23020248194abbee8091cc624f39a5a6d7ccba338b172d5d2d3df0cceffbee android.hardware.neuralnetworks@1.3::IPreparedModel 0439a1fbbec7f16e5e4c653d85ac685d51bfafbae15b8f8cca530acdd7d6a8ce android.hardware.neuralnetworks@1.3::IPreparedModelCallback -162515505235bc770601f02c3537f9ccf11582583bf7b11dd2ec81fab6855333 android.hardware.neuralnetworks@1.3::types +26c643aedf4e28b8d82e517d9cd70601b37f881e1ea94f09808d9e233517e400 android.hardware.neuralnetworks@1.3::types 3e01d4446cd69fd1c48f8572efd97487bc179564b32bd795800b97bbe10be37b android.hardware.wifi@1.4::IWifi a64467bae843569f0d465c5be7f0c7a5b987985b55a3ef4794dd5afc68538650 android.hardware.wifi.supplicant@1.3::ISupplicant 44445b8a03d7b9e68b2fbd954672c18a8fce9e32851b0692f4f4ab3407f86ecb android.hardware.wifi.supplicant@1.3::ISupplicantStaIface diff --git a/neuralnetworks/1.3/types.hal b/neuralnetworks/1.3/types.hal index 0f51b1a048..6a852d14a6 100644 --- a/neuralnetworks/1.3/types.hal +++ b/neuralnetworks/1.3/types.hal @@ -4986,6 +4986,56 @@ enum OperationType : int32_t { */ WHILE = 97, + /** + * Computes exponential linear activation on the input tensor element-wise. + * + * The output is calculated using the following formula: + * + * ELU(x) = max(0, x) + min(0, alpha * (exp(x) - 1)) + * + * Supported tensor {@link OperandType}: + * * {@link OperandType::TENSOR_FLOAT16} + * * {@link OperandType::TENSOR_FLOAT32} + * + * Inputs: + * * 0: A tensor, specifying the input. May be zero-sized. + * * 1: A scalar, specifying the alpha parameter. + * For input tensor of {@link OperandType::TENSOR_FLOAT16}, + * the alpha value must be of {@link OperandType::FLOAT16}. + * For input tensor of {@link OperandType::TENSOR_FLOAT32}, + * the alpha value must be of {@link OperandType::FLOAT32}. + * + * Outputs: + * * 0: The output tensor of same shape and type as input0. + */ + ELU = 98, + + /** + * Computes hard-swish activation on the input tensor element-wise. + * + * Hard swish activation is introduced in + * https://arxiv.org/pdf/1905.02244.pdf + * + * The output is calculated using the following formula: + * + * h-swish(x) = x * max(0, min(6, (x + 3))) / 6 + + * Supported tensor {@link OperandType}: + * * {@link OperandType::TENSOR_FLOAT16} + * * {@link OperandType::TENSOR_FLOAT32} + * * {@link OperandType::TENSOR_QUANT8_ASYMM} + * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} + * + * Inputs: + * * 0: A tensor, specifying the input. May be zero-sized. + * + * Outputs: + * * 0: The output tensor of same shape and type as input0. + * Scale and zero point of this tensor may be different from the input + * tensor's parameters. + */ + HARD_SWISH = 99, + /** * DEPRECATED. Since NNAPI 1.2, extensions are the preferred alternative to * OEM operation and data types. @@ -5008,7 +5058,7 @@ enum OperationType : int32_t { enum OperationTypeRange : uint32_t { BASE_MIN = 0, FUNDAMENTAL_MIN = 0, - FUNDAMENTAL_MAX = 97, + FUNDAMENTAL_MAX = 99, OEM_MIN = 10000, OEM_MAX = 10000, BASE_MAX = 0xFFFF, From 53a51cb352d235ee66de721ca7f743e9d6031f58 Mon Sep 17 00:00:00 2001 From: Lev Proleev Date: Mon, 20 Jan 2020 18:54:46 +0000 Subject: [PATCH 3/6] Add FILL and RANK ops Bug: 148050168 Bug: 148049333 Test: NNTest_static and VtsHalNeuralnetworksV1_3TargetTest Change-Id: Iebdfa600d84e31532807740c21d95cae41c76ad5 Merged-In: Iebdfa600d84e31532807740c21d95cae41c76ad5 (cherry picked from commit 8b3f240ea3864debfc2c6f561029faf23d179f59) --- current.txt | 2 +- neuralnetworks/1.3/types.hal | 52 ++++++++++++++++++- .../vts/functional/GeneratedTestHarness.cpp | 5 +- .../1.3/vts/functional/ValidateModel.cpp | 15 ++++++ 4 files changed, 70 insertions(+), 4 deletions(-) diff --git a/current.txt b/current.txt index d615eaa720..aaef74056a 100644 --- a/current.txt +++ b/current.txt @@ -627,7 +627,7 @@ d1f382d14e1384b907d5bb5780df7f01934650d556fedbed2f15a90773c657d6 android.hardwar 4167dc3ad35e9cd0d2057d4868c7675ae2c3c9d05bbd614c1f5dccfa5fd68797 android.hardware.neuralnetworks@1.3::IExecutionCallback 7d23020248194abbee8091cc624f39a5a6d7ccba338b172d5d2d3df0cceffbee android.hardware.neuralnetworks@1.3::IPreparedModel 0439a1fbbec7f16e5e4c653d85ac685d51bfafbae15b8f8cca530acdd7d6a8ce android.hardware.neuralnetworks@1.3::IPreparedModelCallback -26c643aedf4e28b8d82e517d9cd70601b37f881e1ea94f09808d9e233517e400 android.hardware.neuralnetworks@1.3::types +5f1a4e0c29fc686ed476f9f04eed35e4405d21288cb2746b978d6891de5cc37d android.hardware.neuralnetworks@1.3::types 3e01d4446cd69fd1c48f8572efd97487bc179564b32bd795800b97bbe10be37b android.hardware.wifi@1.4::IWifi a64467bae843569f0d465c5be7f0c7a5b987985b55a3ef4794dd5afc68538650 android.hardware.wifi.supplicant@1.3::ISupplicant 44445b8a03d7b9e68b2fbd954672c18a8fce9e32851b0692f4f4ab3407f86ecb android.hardware.wifi.supplicant@1.3::ISupplicantStaIface diff --git a/neuralnetworks/1.3/types.hal b/neuralnetworks/1.3/types.hal index 6a852d14a6..abc33e77d3 100644 --- a/neuralnetworks/1.3/types.hal +++ b/neuralnetworks/1.3/types.hal @@ -5036,6 +5036,56 @@ enum OperationType : int32_t { */ HARD_SWISH = 99, + /** + * Creates a tensor filled with a scalar value. + * + * Supported output tensor {@link OperandType}: + * * {@link OperandType::TENSOR_FLOAT16} + * * {@link OperandType::TENSOR_FLOAT32} + * * {@link OperandType::TENSOR_INT32} + * + * Inputs: + * * 0: A 1-D tensor, specifying the desired output tensor shape. + * * 1: A scalar, specifying the value to fill the output tensors with. + * For output tensor of {@link OperandType::TENSOR_FLOAT16}, + * the scalar must be of {@link OperandType::FLOAT16}. + * For output tensor of {@link OperandType::TENSOR_FLOAT32}, + * the scalar must be of {@link OperandType::FLOAT32}. + * For output tensor of {@link OperandType::TENSOR_INT32}, + * the scalar must be of {@link OperandType::INT32}. + * + * Outputs: + * * 0: The output tensor. + */ + FILL = 100, + + /** + * Returns the rank of a tensor. + * + * The rank of a tensor is the number of dimensions in it. Also known as + * "order", "degree", "ndims". + * + * Supported tensor {@link OperandType}: + * * {@link OperandType::TENSOR_FLOAT16} + * * {@link OperandType::TENSOR_FLOAT32} + * * {@link OperandType::TENSOR_INT32} + * * {@link OperandType::TENSOR_QUANT8_ASYMM} + * * {@link OperandType::TENSOR_QUANT16_SYMM} + * * {@link OperandType::TENSOR_BOOL8} + * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} + * * {@link OperandType::TENSOR_QUANT16_ASYMM} + * * {@link OperandType::TENSOR_QUANT8_SYMM} + * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} + * + * Inputs: + * * 0: The input tensor. + * + * Outputs: + * * 0: A scalar of {@link OperandType::INT32}, specifying the rank + * of the input tensor. + */ + RANK = 101, + /** * DEPRECATED. Since NNAPI 1.2, extensions are the preferred alternative to * OEM operation and data types. @@ -5058,7 +5108,7 @@ enum OperationType : int32_t { enum OperationTypeRange : uint32_t { BASE_MIN = 0, FUNDAMENTAL_MIN = 0, - FUNDAMENTAL_MAX = 99, + FUNDAMENTAL_MAX = 101, OEM_MIN = 10000, OEM_MAX = 10000, BASE_MAX = 0xFFFF, diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp index a2c0c4efa0..cba1f7766c 100644 --- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp +++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp @@ -793,8 +793,9 @@ TEST_P(QuantizationCouplingTest, Test) { INSTANTIATE_GENERATED_TEST(GeneratedTest, [](const TestModel& testModel) { return !testModel.expectFailure; }); -INSTANTIATE_GENERATED_TEST(DynamicOutputShapeTest, - [](const TestModel& testModel) { return !testModel.expectFailure; }); +INSTANTIATE_GENERATED_TEST(DynamicOutputShapeTest, [](const TestModel& testModel) { + return !testModel.expectFailure && !testModel.hasScalarOutputs(); +}); INSTANTIATE_GENERATED_TEST(MemoryDomainTest, [](const TestModel& testModel) { return !testModel.expectFailure; }); diff --git a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp index a21142880e..1245432307 100644 --- a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp +++ b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp @@ -337,6 +337,7 @@ static bool mutateOperationOperandTypeSkip(size_t operand, OperandType type, con // - TRANSPOSE_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL // - AXIS_ALIGNED_BBOX_TRANSFORM bounding boxes (arg 1) can be of // TENSOR_QUANT8_ASYMM or TENSOR_QUANT8_ASYMM_SIGNED. + // - RANK's input can have any TENSOR_* type. switch (operation.type) { case OperationType::LSH_PROJECTION: { if (operand == operation.inputs[1]) { @@ -399,6 +400,20 @@ static bool mutateOperationOperandTypeSkip(size_t operand, OperandType type, con return true; } } break; + case OperationType::RANK: { + if (operand == operation.inputs[0] && + (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32 || + type == OperandType::TENSOR_INT32 || + type == OperandType::TENSOR_QUANT8_ASYMM || + type == OperandType::TENSOR_QUANT16_SYMM || + type == OperandType::TENSOR_BOOL8 || + type == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL || + type == OperandType::TENSOR_QUANT16_ASYMM || + type == OperandType::TENSOR_QUANT8_SYMM || + type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED)) { + return true; + } + } break; default: break; } From 12ea0d4ed6b2dac5c89fd67739fc6375aa042e3d Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Tue, 12 Nov 2019 14:02:16 -0800 Subject: [PATCH 4/6] Support sync fence in NNAPI - Add IPreparedModel::dispatchRequest to NNAPI 1.3 HAL - Add IDispatchExecutionCallback to allow clients query information related to the actual evaluation. Bug: 142778241 Test: mm Change-Id: I87cbb7f2aee87342b0418fce04eb4050e2bc1920 Merged-In: I87cbb7f2aee87342b0418fce04eb4050e2bc1920 (cherry picked from commit 90cf3dd37ce08973c7a709a951bc444470a0d20a) --- current.txt | 3 +- neuralnetworks/1.3/Android.bp | 1 + .../1.3/IFencedExecutionCallback.hal | 48 ++++++++++++++++ neuralnetworks/1.3/IPreparedModel.hal | 57 ++++++++++++++++++- 4 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 neuralnetworks/1.3/IFencedExecutionCallback.hal diff --git a/current.txt b/current.txt index aaef74056a..4f45f1f6c2 100644 --- a/current.txt +++ b/current.txt @@ -625,7 +625,8 @@ ac429fca0da4ce91218768ec31b64ded88251f8a26d8c4f27c06abdc5b1926d9 android.hardwar 65c16331e57f6dd68b3971f06f78fe9e3209afb60630c31705aa355f9a52bf0d android.hardware.neuralnetworks@1.3::IBuffer d1f382d14e1384b907d5bb5780df7f01934650d556fedbed2f15a90773c657d6 android.hardware.neuralnetworks@1.3::IDevice 4167dc3ad35e9cd0d2057d4868c7675ae2c3c9d05bbd614c1f5dccfa5fd68797 android.hardware.neuralnetworks@1.3::IExecutionCallback -7d23020248194abbee8091cc624f39a5a6d7ccba338b172d5d2d3df0cceffbee android.hardware.neuralnetworks@1.3::IPreparedModel +29e26e83399b69c7998b787bd30426dd5baa2da350effca76bbee1ba877355c9 android.hardware.neuralnetworks@1.3::IFencedExecutionCallback +384fd9fd6e4d43ea11d407e52ea81da5242c3c5f4b458b8707d8feb652a13e36 android.hardware.neuralnetworks@1.3::IPreparedModel 0439a1fbbec7f16e5e4c653d85ac685d51bfafbae15b8f8cca530acdd7d6a8ce android.hardware.neuralnetworks@1.3::IPreparedModelCallback 5f1a4e0c29fc686ed476f9f04eed35e4405d21288cb2746b978d6891de5cc37d android.hardware.neuralnetworks@1.3::types 3e01d4446cd69fd1c48f8572efd97487bc179564b32bd795800b97bbe10be37b android.hardware.wifi@1.4::IWifi diff --git a/neuralnetworks/1.3/Android.bp b/neuralnetworks/1.3/Android.bp index 56011e227d..7b02cc510f 100644 --- a/neuralnetworks/1.3/Android.bp +++ b/neuralnetworks/1.3/Android.bp @@ -11,6 +11,7 @@ hidl_interface { "IBuffer.hal", "IDevice.hal", "IExecutionCallback.hal", + "IFencedExecutionCallback.hal", "IPreparedModel.hal", "IPreparedModelCallback.hal", ], diff --git a/neuralnetworks/1.3/IFencedExecutionCallback.hal b/neuralnetworks/1.3/IFencedExecutionCallback.hal new file mode 100644 index 0000000000..39076b9a16 --- /dev/null +++ b/neuralnetworks/1.3/IFencedExecutionCallback.hal @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.neuralnetworks@1.3; + +import @1.2::Timing; +import ErrorStatus; + +/** + * IFencedExecutionCallback can be used to query the error status result + * and duration information from an IPreparedModel::executeFenced call. + */ +interface IFencedExecutionCallback { + + /** + * The getExecutionInfo method is used by the clients to query error status + * result and duration information. The method must only be called after the actual + * evaluation has finished or resulted in an runtime error, as indicated by the status + * of the sync fence returned by the IPreparedModel::executeFenced call, otherwise + * GENERAL_FAILURE must be returned. + * + * @return status Error status returned from the asynchronously dispatched execution + * must be: + * - NONE if the asynchronous execution was successful + * - DEVICE_UNAVAILABLE if driver is offline or busy + * - GENERAL_FAILURE if the asynchronous task resulted in an + * unspecified error + * @return timing Duration of execution. Unless MeasureTiming::YES was passed when + * launching the execution and status is NONE, all times must + * be reported as UINT64_MAX. A driver may choose to report + * any time as UINT64_MAX, indicating that particular measurement is + * not available. + */ + getExecutionInfo() generates (ErrorStatus status, Timing timing); +}; diff --git a/neuralnetworks/1.3/IPreparedModel.hal b/neuralnetworks/1.3/IPreparedModel.hal index bce6ee227a..f84bcf4ffc 100644 --- a/neuralnetworks/1.3/IPreparedModel.hal +++ b/neuralnetworks/1.3/IPreparedModel.hal @@ -24,6 +24,7 @@ import ErrorStatus; import OptionalTimePoint; import Request; import IExecutionCallback; +import IFencedExecutionCallback; /** * IPreparedModel describes a model that has been prepared for execution and @@ -91,7 +92,8 @@ interface IPreparedModel extends @1.2::IPreparedModel { * execution cannot be finished by the deadline, the * execution must be aborted. * @param callback A callback object used to return the error status of - * the execution. The callback object's notify function must + * the execution, shape information of model output operands, and + * duration of execution. The callback object's notify function must * be called exactly once, even if the execution was * unsuccessful. * @return status Error status of the call, must be: @@ -187,4 +189,57 @@ interface IPreparedModel extends @1.2::IPreparedModel { OptionalTimePoint deadline) generates (ErrorStatus status, vec outputShapes, Timing timing); + + /** + * Launch a fenced asynchronous execution on a prepared model. + * + * The execution is performed asynchronously with respect to the caller. + * executeFenced must fully validate the request, and only accept one that is + * guaranteed to be completed, unless a hardware failure or kernel panic happens on the device. + * If there is an error during validation, executeFenced must immediately return with + * the corresponding ErrorStatus. If the request is valid and there is no error launching, + * executeFenced must dispatch an asynchronous task to perform the execution in the + * background, and immediately return with ErrorStatus::NONE, a sync_fence that will be + * signaled once the execution is completed, and a callback that can be used by the client + * to query the duration and runtime error status. If the task has finished + * before the call returns, empty handle may be returned for the sync fence. If the + * asynchronous task fails to launch, executeFenced must immediately return with + * ErrorStatus::GENERAL_FAILURE, and empty handle for the sync fence and nullptr + * for callback. The execution must wait for all the sync fences (if any) in wait_for to be + * signaled before starting the actual execution. + * + * If any of sync fences in wait_for changes to error status after the executeFenced + * call succeeds, the driver must immediately set the returned sync fence to error status. + * + * When the asynchronous task has finished its execution, it must + * immediately signal the sync_fence created when dispatching. After + * the sync_fence is signaled, the task must not modify the content of + * any data object referenced by 'request' (described by the + * {@link @1.0::DataLocation} of a {@link @1.0::RequestArgument}). + * + * Any number of calls to the executeFenced, execute* and executeSynchronously* + * functions, in any combination, may be made concurrently, even on the same + * IPreparedModel object. + * + * @param request The input and output information on which the prepared + * model is to be executed. + * @param waitFor A vector of sync fence file descriptors. + * Execution must not start until all sync fences have been signaled. + * @param measure Specifies whether or not to measure duration of the execution. + * The duration runs from the time the driver sees the call + * to the executeFenced function to the time sync_fence is triggered. + * @return status Error status of the call, must be: + * - NONE if task is successfully launched + * - 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. + * @return syncFence The sync fence that will be triggered when the task is completed. + * The sync fence will be set to error if a critical error, + * e.g. hardware failure or kernel panic, occurs when doing execution. + * @return callback The IFencedExecutionCallback can be used to query information like duration + * and error status when the execution is completed. + */ + executeFenced(Request request, vec waitFor, MeasureTiming measure) + generates (ErrorStatus status, handle syncFence, IFencedExecutionCallback callback); }; From 2b5c4cda777646fba5152de46d6a4bbf27cba627 Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Thu, 26 Dec 2019 18:03:56 -0800 Subject: [PATCH 5/6] Add VTS tests for NNAPI IPreparedModel::executeFenced - Validation tests - Generated tests to exercise executeFenced path Bug: 142778241 Test: mm Change-Id: I509f0b5713fc86885d597940aae5ade0502c97ad Merged-In: I509f0b5713fc86885d597940aae5ade0502c97ad (cherry picked from commit 2c4e02329b6bcc33e3d520f3cc4e80ab7b83cf4a) --- neuralnetworks/1.3/vts/functional/Android.bp | 1 + .../vts/functional/GeneratedTestHarness.cpp | 46 +++++++++++++++++-- .../1.3/vts/functional/ValidateRequest.cpp | 18 ++++++++ .../vts/functional/VtsHalNeuralnetworks.cpp | 30 ++++++++++++ 4 files changed, 92 insertions(+), 3 deletions(-) diff --git a/neuralnetworks/1.3/vts/functional/Android.bp b/neuralnetworks/1.3/vts/functional/Android.bp index ce2d3a917a..8e7e9b9d62 100644 --- a/neuralnetworks/1.3/vts/functional/Android.bp +++ b/neuralnetworks/1.3/vts/functional/Android.bp @@ -65,6 +65,7 @@ cc_test { "libhidlmemory", "libneuralnetworks_generated_test_harness", "libneuralnetworks_utils", + "libsync", ], whole_static_libs: [ "neuralnetworks_generated_V1_0_example", diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp index cba1f7766c..b8111492f0 100644 --- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp +++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp @@ -29,11 +29,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -70,7 +72,7 @@ using HidlToken = hidl_array(Constant::BYTE_SIZE_ namespace { -enum class Executor { ASYNC, SYNC, BURST }; +enum class Executor { ASYNC, SYNC, BURST, FENCED }; enum class OutputType { FULLY_SPECIFIED, UNSPECIFIED, INSUFFICIENT }; @@ -562,6 +564,43 @@ void EvaluatePreparedModel(const sp& device, const sp& break; } + case Executor::FENCED: { + SCOPED_TRACE("fenced"); + ErrorStatus result; + hidl_handle sync_fence_handle; + sp fenced_callback; + Return ret = preparedModel->executeFenced( + request, {}, testConfig.measureTiming, + [&result, &sync_fence_handle, &fenced_callback]( + ErrorStatus error, const hidl_handle& handle, + const sp& callback) { + result = error; + sync_fence_handle = handle; + fenced_callback = callback; + }); + ASSERT_TRUE(ret.isOk()); + if (result != ErrorStatus::NONE) { + ASSERT_EQ(sync_fence_handle.getNativeHandle(), nullptr); + ASSERT_EQ(fenced_callback, nullptr); + executionStatus = ErrorStatus::GENERAL_FAILURE; + } else if (sync_fence_handle.getNativeHandle()) { + constexpr int kInfiniteTimeout = -1; + int sync_fd = sync_fence_handle.getNativeHandle()->data[0]; + ASSERT_GT(sync_fd, 0); + int r = sync_wait(sync_fd, kInfiniteTimeout); + ASSERT_GE(r, 0); + } + if (result == ErrorStatus::NONE) { + ASSERT_NE(fenced_callback, nullptr); + Return ret = fenced_callback->getExecutionInfo( + [&executionStatus, &timing](ErrorStatus error, Timing t) { + executionStatus = error; + timing = t; + }); + ASSERT_TRUE(ret.isOk()); + } + break; + } } if (testConfig.outputType != OutputType::FULLY_SPECIFIED && @@ -635,7 +674,7 @@ void EvaluatePreparedModel(const sp& device, const sp& case TestKind::GENERAL: { outputTypesList = {OutputType::FULLY_SPECIFIED}; measureTimingList = {MeasureTiming::NO, MeasureTiming::YES}; - executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST}; + executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST, Executor::FENCED}; } break; case TestKind::DYNAMIC_SHAPE: { outputTypesList = {OutputType::UNSPECIFIED, OutputType::INSUFFICIENT}; @@ -671,7 +710,8 @@ void EvaluatePreparedCoupledModels(const sp& device, const TestModel& coupledModel) { const std::vector outputTypesList = {OutputType::FULLY_SPECIFIED}; const std::vector measureTimingList = {MeasureTiming::NO, MeasureTiming::YES}; - const std::vector executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST}; + const std::vector executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST, + Executor::FENCED}; for (const OutputType outputType : outputTypesList) { for (const MeasureTiming measureTiming : measureTimingList) { diff --git a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp index be4112ac2d..1ddd09c033 100644 --- a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp +++ b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp @@ -16,7 +16,9 @@ #define LOG_TAG "neuralnetworks_hidl_hal_test" +#include #include + #include "1.0/Utils.h" #include "1.3/Callbacks.h" #include "ExecutionBurstController.h" @@ -136,6 +138,22 @@ static void validate(const sp& preparedModel, const std::string& burst->freeMemory(keys.front()); } } + + // dispatch + { + SCOPED_TRACE(message + " [executeFenced]"); + Return ret = preparedModel->executeFenced( + request, {}, MeasureTiming::NO, + [](ErrorStatus error, const hidl_handle& handle, + const sp& callback) { + if (error != ErrorStatus::DEVICE_UNAVAILABLE) { + ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error); + } + ASSERT_EQ(handle.getNativeHandle(), nullptr); + ASSERT_EQ(callback, nullptr); + }); + ASSERT_TRUE(ret.isOk()); + } } ///////////////////////// REMOVE INPUT //////////////////////////////////// diff --git a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp index 93c8f13c17..28cc8ffe65 100644 --- a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp +++ b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp @@ -133,6 +133,35 @@ void validateRequestFailure(const sp& preparedModel, const Reque // Forward declaration from ValidateBurst.cpp void validateBurst(const sp& preparedModel, const V1_0::Request& request); +// Validate sync_fence handles for dispatch with valid input +void validateExecuteFenced(const sp& preparedModel, const Request& request) { + SCOPED_TRACE("Expecting request to fail [executeFenced]"); + Return ret_null = + preparedModel->executeFenced(request, {hidl_handle(nullptr)}, V1_2::MeasureTiming::NO, + [](ErrorStatus error, const hidl_handle& handle, + const sp& callback) { + ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error); + ASSERT_EQ(handle.getNativeHandle(), nullptr); + ASSERT_EQ(callback, nullptr); + }); + ASSERT_TRUE(ret_null.isOk()); + + native_handle_t* nativeHandle = native_handle_create(1, 0); + ASSERT_NE(nullptr, nativeHandle); + nativeHandle->data[0] = -1; + hidl_handle hidlHandle; + hidlHandle.setTo(nativeHandle, /*shouldOwn=*/true); + Return ret_invalid = + preparedModel->executeFenced(request, {hidlHandle}, V1_2::MeasureTiming::NO, + [](ErrorStatus error, const hidl_handle& handle, + const sp& callback) { + ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error); + ASSERT_EQ(handle.getNativeHandle(), nullptr); + ASSERT_EQ(callback, nullptr); + }); + ASSERT_TRUE(ret_invalid.isOk()); +} + void validateEverything(const sp& device, const Model& model, const Request& request, std::pair supportsDeadlines) { const auto [prepareModelDeadlineSupported, executionDeadlineSupported] = supportsDeadlines; @@ -144,6 +173,7 @@ void validateEverything(const sp& device, const Model& model, const Req if (preparedModel == nullptr) return; validateRequest(preparedModel, request, executionDeadlineSupported); + validateExecuteFenced(preparedModel, request); // TODO(butlermichael): Check if we need to test burst in V1_3 if the interface remains V1_2. ASSERT_TRUE(nn::compliantWithV1_0(request)); From e51e4a9f26d9afbccfe9300eea27cc065eb309c8 Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Thu, 23 Jan 2020 13:53:21 -0800 Subject: [PATCH 6/6] Fix the NNAPI vts tests about validateExecuteFenced - Skip the test if the driver reject executeFenced. - remove the -1 fd for hidl_handle as it causes error before reaching the driver. Test: mm Test: VtsHalNeuralnetworksV1_3TargetTest Change-Id: Idc3f815040efccbbfa95b70d5d437441d0bd8682 Merged-In: Idc3f815040efccbbfa95b70d5d437441d0bd8682 (cherry picked from commit 33173a3e685c212b86706dd242e14804bee130f6) --- .../vts/functional/GeneratedTestHarness.cpp | 24 ++++++++++++++++--- .../1.3/vts/functional/GeneratedTestHarness.h | 2 ++ .../vts/functional/VtsHalNeuralnetworks.cpp | 20 ++++------------ 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp index b8111492f0..88837db349 100644 --- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp +++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp @@ -603,7 +603,9 @@ void EvaluatePreparedModel(const sp& device, const sp& } } - if (testConfig.outputType != OutputType::FULLY_SPECIFIED && + // The driver is allowed to reject executeFenced, and if they do, we should skip. + if ((testConfig.outputType != OutputType::FULLY_SPECIFIED || + testConfig.executor == Executor::FENCED) && executionStatus == ErrorStatus::GENERAL_FAILURE) { if (skipped != nullptr) { *skipped = true; @@ -674,7 +676,7 @@ void EvaluatePreparedModel(const sp& device, const sp& case TestKind::GENERAL: { outputTypesList = {OutputType::FULLY_SPECIFIED}; measureTimingList = {MeasureTiming::NO, MeasureTiming::YES}; - executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST, Executor::FENCED}; + executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST}; } break; case TestKind::DYNAMIC_SHAPE: { outputTypesList = {OutputType::UNSPECIFIED, OutputType::INSUFFICIENT}; @@ -687,6 +689,11 @@ void EvaluatePreparedModel(const sp& device, const sp& executorList = {Executor::ASYNC, Executor::SYNC}; memoryType = MemoryType::DEVICE; } break; + case TestKind::FENCED_COMPUTE: { + outputTypesList = {OutputType::FULLY_SPECIFIED}; + measureTimingList = {MeasureTiming::NO, MeasureTiming::YES}; + executorList = {Executor::FENCED}; + } break; case TestKind::QUANTIZATION_COUPLING: { LOG(FATAL) << "Wrong TestKind for EvaluatePreparedModel"; return; @@ -748,7 +755,8 @@ void Execute(const sp& device, const TestModel& testModel, TestKind tes switch (testKind) { case TestKind::GENERAL: case TestKind::DYNAMIC_SHAPE: - case TestKind::MEMORY_DOMAIN: { + case TestKind::MEMORY_DOMAIN: + case TestKind::FENCED_COMPUTE: { createPreparedModel(device, model, &preparedModel); if (preparedModel == nullptr) return; EvaluatePreparedModel(device, preparedModel, testModel, testKind); @@ -811,6 +819,9 @@ class DynamicOutputShapeTest : public GeneratedTest {}; // Tag for the memory domain tests class MemoryDomainTest : public GeneratedTest {}; +// Tag for the fenced compute tests +class FencedComputeTest : public GeneratedTest {}; + // Tag for the dynamic output shape tests class QuantizationCouplingTest : public GeneratedTest {}; @@ -826,6 +837,10 @@ TEST_P(MemoryDomainTest, Test) { Execute(kDevice, kTestModel, /*testKind=*/TestKind::MEMORY_DOMAIN); } +TEST_P(FencedComputeTest, Test) { + Execute(kDevice, kTestModel, /*testKind=*/TestKind::FENCED_COMPUTE); +} + TEST_P(QuantizationCouplingTest, Test) { Execute(kDevice, kTestModel, /*testKind=*/TestKind::QUANTIZATION_COUPLING); } @@ -840,6 +855,9 @@ INSTANTIATE_GENERATED_TEST(DynamicOutputShapeTest, [](const TestModel& testModel INSTANTIATE_GENERATED_TEST(MemoryDomainTest, [](const TestModel& testModel) { return !testModel.expectFailure; }); +INSTANTIATE_GENERATED_TEST(FencedComputeTest, + [](const TestModel& testModel) { return !testModel.expectFailure; }); + INSTANTIATE_GENERATED_TEST(QuantizationCouplingTest, [](const TestModel& testModel) { return testModel.hasQuant8CoupledOperands() && testModel.operations.size() == 1; }); diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h index fe695b471d..e597fac7cf 100644 --- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h +++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h @@ -65,6 +65,8 @@ enum class TestKind { DYNAMIC_SHAPE, // Same as GENERAL but use device memories for inputs and outputs MEMORY_DOMAIN, + // Same as GENERAL but use executeFenced for exeuction + FENCED_COMPUTE, // Tests if quantized model with TENSOR_QUANT8_ASYMM produces the same result // (OK/SKIPPED/FAILED) as the model with all such tensors converted to // TENSOR_QUANT8_ASYMM_SIGNED. diff --git a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp index 28cc8ffe65..c84f5b70e7 100644 --- a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp +++ b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp @@ -140,26 +140,14 @@ void validateExecuteFenced(const sp& preparedModel, const Reques preparedModel->executeFenced(request, {hidl_handle(nullptr)}, V1_2::MeasureTiming::NO, [](ErrorStatus error, const hidl_handle& handle, const sp& callback) { - ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error); + // TODO: fix this once sample driver impl is merged. + if (error != ErrorStatus::DEVICE_UNAVAILABLE) { + ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error); + } ASSERT_EQ(handle.getNativeHandle(), nullptr); ASSERT_EQ(callback, nullptr); }); ASSERT_TRUE(ret_null.isOk()); - - native_handle_t* nativeHandle = native_handle_create(1, 0); - ASSERT_NE(nullptr, nativeHandle); - nativeHandle->data[0] = -1; - hidl_handle hidlHandle; - hidlHandle.setTo(nativeHandle, /*shouldOwn=*/true); - Return ret_invalid = - preparedModel->executeFenced(request, {hidlHandle}, V1_2::MeasureTiming::NO, - [](ErrorStatus error, const hidl_handle& handle, - const sp& callback) { - ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error); - ASSERT_EQ(handle.getNativeHandle(), nullptr); - ASSERT_EQ(callback, nullptr); - }); - ASSERT_TRUE(ret_invalid.isOk()); } void validateEverything(const sp& device, const Model& model, const Request& request,