Add 1.2 NN HAL interface for dynamic output shape.
Let notify_1_2() notify output shapes. Document unspecified dimensions and rank. Bug: 73506513 Bug: 77234888 Test: NeuralNetworksTest_static Test: VtsHalNeuralnetworksV1_xTargetTest with 1.2 sample driver Change-Id: I01108913212d9f4aa47daf2f293ea19259925865
This commit is contained in:
parent
a885b5d982
commit
187c59715c
8 changed files with 141 additions and 34 deletions
|
@ -139,8 +139,10 @@ Return<void> ExecutionCallback::notify(ErrorStatus errorStatus) {
|
|||
return Void();
|
||||
}
|
||||
|
||||
Return<void> ExecutionCallback::notify_1_2(ErrorStatus errorStatus) {
|
||||
Return<void> ExecutionCallback::notify_1_2(ErrorStatus errorStatus,
|
||||
const hidl_vec<OutputShape>& outputShapes) {
|
||||
mErrorStatus = errorStatus;
|
||||
mOutputShapes = outputShapes;
|
||||
CallbackBase::notify();
|
||||
return Void();
|
||||
}
|
||||
|
@ -150,6 +152,11 @@ ErrorStatus ExecutionCallback::getStatus() {
|
|||
return mErrorStatus;
|
||||
}
|
||||
|
||||
const std::vector<OutputShape>& ExecutionCallback::getOutputShapes() {
|
||||
wait();
|
||||
return mOutputShapes;
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_2
|
||||
} // namespace neuralnetworks
|
||||
|
|
|
@ -275,8 +275,9 @@ class ExecutionCallback : public CallbackBase, public IExecutionCallback {
|
|||
* Either IExecutionCallback::notify or IExecutionCallback::notify_1_2 must
|
||||
* be called exactly once on a given ExecutionCallback object.
|
||||
*
|
||||
* @param status Error status returned from asynchronously preparing the
|
||||
* model; will be:
|
||||
* @param status Error status returned from launching the asynchronous task
|
||||
* (if the launch fails) or from the asynchronous task itself
|
||||
* (if the launch succeeds). Must be:
|
||||
* - NONE if the asynchronous execution was successful
|
||||
* - DEVICE_UNAVAILABLE if driver is offline or busy
|
||||
* - GENERAL_FAILURE if there is an unspecified error
|
||||
|
@ -285,27 +286,73 @@ class ExecutionCallback : public CallbackBase, public IExecutionCallback {
|
|||
* - INVALID_ARGUMENT if the input request is invalid
|
||||
*/
|
||||
Return<void> notify(ErrorStatus status) override;
|
||||
Return<void> notify_1_2(ErrorStatus status) override;
|
||||
|
||||
/**
|
||||
* Similar to IExecutionCallback::notify, but for V1_2::IPreparedModel to
|
||||
* also notify output shapes along with error status.
|
||||
*
|
||||
* @param status Error status returned from launching the asynchronous task
|
||||
* (if the launch fails) or from the asynchronous task itself
|
||||
* (if the launch succeeds). 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
|
||||
* - OUTPUT_INSUFFICIENT_SIZE if at least one output
|
||||
* operand buffer is not large enough to store the
|
||||
* corresponding output
|
||||
* - INVALID_ARGUMENT if one of the input arguments to
|
||||
* prepareModel is invalid
|
||||
* @param outputShapes A list of shape information of model output operands.
|
||||
* The index into "outputShapes" corresponds to the index
|
||||
* of the output operand in the Request outputs vector.
|
||||
* outputShapes must be empty unless the status is either
|
||||
* NONE or OUTPUT_INSUFFICIENT_SIZE.
|
||||
*/
|
||||
Return<void> notify_1_2(ErrorStatus status, const hidl_vec<OutputShape>& outputShapes) override;
|
||||
|
||||
/**
|
||||
* Retrieves the error status returned from the asynchronous task launched
|
||||
* by IPreparedModel::execute. If IPreparedModel::execute has not finished
|
||||
* by either IPreparedModel::execute or IPreparedModel::execute_1_2. If
|
||||
* IPreparedModel::execute or IPreparedModel::execute_1_2 has not finished
|
||||
* asynchronously executing, this call will block until the asynchronous task
|
||||
* notifies the object.
|
||||
*
|
||||
* @return status Error status returned from asynchronously preparing the
|
||||
* model; will be:
|
||||
* @return status Error status returned from launching the asynchronous task
|
||||
* (if the launch fails) or from the asynchronous task itself
|
||||
* (if the launch succeeds). Must be:
|
||||
* - NONE if the asynchronous execution was successful
|
||||
* - DEVICE_UNAVAILABLE if driver is offline or busy
|
||||
* - GENERAL_FAILURE if there is an unspecified error
|
||||
* - OUTPUT_INSUFFICIENT_SIZE if provided output buffer is
|
||||
* not large enough to store the resultant values
|
||||
* - INVALID_ARGUMENT if the input request is invalid
|
||||
* - GENERAL_FAILURE if the asynchronous task resulted in an
|
||||
* unspecified error
|
||||
* - OUTPUT_INSUFFICIENT_SIZE if at least one output
|
||||
* operand buffer is not large enough to store the
|
||||
* corresponding output
|
||||
* - INVALID_ARGUMENT if one of the input arguments to
|
||||
* prepareModel is invalid
|
||||
*/
|
||||
ErrorStatus getStatus();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Retrieves the output shapes returned from the asynchronous task launched
|
||||
* by IPreparedModel::execute_1_2. If IPreparedModel::execute_1_2 has not finished
|
||||
* asynchronously executing, this call will block until the asynchronous task
|
||||
* notifies the object.
|
||||
*
|
||||
* If the asynchronous task was launched by IPreparedModel::execute, an empty vector
|
||||
* will be returned.
|
||||
*
|
||||
* @return outputShapes A list of shape information of model output operands.
|
||||
* The index into "outputShapes" corresponds to the index
|
||||
* of the output operand in the Request outputs vector.
|
||||
* outputShapes must be empty unless the status is either
|
||||
* NONE or OUTPUT_INSUFFICIENT_SIZE.
|
||||
*/
|
||||
const std::vector<OutputShape>& getOutputShapes();
|
||||
|
||||
private:
|
||||
ErrorStatus mErrorStatus;
|
||||
std::vector<OutputShape> mOutputShapes;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -89,13 +89,24 @@ static Return<ErrorStatus> ExecutePreparedModel(sp<V1_2::IPreparedModel>& prepar
|
|||
sp<ExecutionCallback>& callback) {
|
||||
return preparedModel->execute_1_2(request, callback);
|
||||
}
|
||||
static Return<ErrorStatus> ExecutePreparedModel(sp<V1_0::IPreparedModel>&, const Request&) {
|
||||
static Return<ErrorStatus> ExecutePreparedModel(sp<V1_0::IPreparedModel>&, const Request&,
|
||||
hidl_vec<OutputShape>*) {
|
||||
ADD_FAILURE() << "asking for synchronous execution at V1_0";
|
||||
return ErrorStatus::GENERAL_FAILURE;
|
||||
}
|
||||
static Return<ErrorStatus> ExecutePreparedModel(sp<V1_2::IPreparedModel>& preparedModel,
|
||||
const Request& request) {
|
||||
return preparedModel->executeSynchronously(request);
|
||||
const Request& request,
|
||||
hidl_vec<OutputShape>* outputShapes) {
|
||||
ErrorStatus result;
|
||||
Return<void> ret = preparedModel->executeSynchronously(
|
||||
request, [&result, &outputShapes](ErrorStatus error, const hidl_vec<OutputShape>& shapes) {
|
||||
result = error;
|
||||
*outputShapes = shapes;
|
||||
});
|
||||
if (!ret.isOk()) {
|
||||
return ErrorStatus::GENERAL_FAILURE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
enum class Synchronously { NO, YES };
|
||||
const float kDefaultAtol = 1e-5f;
|
||||
|
@ -197,6 +208,8 @@ void EvaluatePreparedModel(sp<T_IPreparedModel>& preparedModel, std::function<bo
|
|||
inputMemory->commit();
|
||||
outputMemory->commit();
|
||||
|
||||
ErrorStatus executionStatus;
|
||||
hidl_vec<OutputShape> outputShapes;
|
||||
if (sync == Synchronously::NO) {
|
||||
SCOPED_TRACE("asynchronous");
|
||||
|
||||
|
@ -211,18 +224,24 @@ void EvaluatePreparedModel(sp<T_IPreparedModel>& preparedModel, std::function<bo
|
|||
|
||||
// retrieve execution status
|
||||
executionCallback->wait();
|
||||
ErrorStatus executionReturnStatus = executionCallback->getStatus();
|
||||
EXPECT_EQ(ErrorStatus::NONE, executionReturnStatus);
|
||||
executionStatus = executionCallback->getStatus();
|
||||
outputShapes = executionCallback->getOutputShapes();
|
||||
} else {
|
||||
SCOPED_TRACE("synchronous");
|
||||
|
||||
// execute
|
||||
Return<ErrorStatus> executionStatus = ExecutePreparedModel(
|
||||
preparedModel, {.inputs = inputs_info, .outputs = outputs_info, .pools = pools});
|
||||
ASSERT_TRUE(executionStatus.isOk());
|
||||
EXPECT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(executionStatus));
|
||||
Return<ErrorStatus> executionReturnStatus = ExecutePreparedModel(
|
||||
preparedModel, {.inputs = inputs_info, .outputs = outputs_info, .pools = pools},
|
||||
&outputShapes);
|
||||
ASSERT_TRUE(executionReturnStatus.isOk());
|
||||
executionStatus = static_cast<ErrorStatus>(executionReturnStatus);
|
||||
}
|
||||
|
||||
ASSERT_EQ(ErrorStatus::NONE, executionStatus);
|
||||
// TODO(xusongw): Check if the returned output shapes match with expectation once the
|
||||
// sample driver implementation of dynamic output shape is finished.
|
||||
ASSERT_EQ(outputShapes.size(), 0);
|
||||
|
||||
// validate results
|
||||
outputMemory->read();
|
||||
copy_back(&test, outputs_info, outputPtr);
|
||||
|
|
|
@ -27,6 +27,7 @@ hidl_interface {
|
|||
"Operation",
|
||||
"OperationType",
|
||||
"OperationTypeRange",
|
||||
"OutputShape",
|
||||
],
|
||||
gen_java: false,
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package android.hardware.neuralnetworks@1.2;
|
|||
|
||||
import @1.0::ErrorStatus;
|
||||
import @1.0::IExecutionCallback;
|
||||
import OutputShape;
|
||||
|
||||
/**
|
||||
* IExecutionCallback must be used to return the error status result from an
|
||||
|
@ -39,10 +40,16 @@ interface IExecutionCallback extends @1.0::IExecutionCallback {
|
|||
* - DEVICE_UNAVAILABLE if driver is offline or busy
|
||||
* - GENERAL_FAILURE if the asynchronous task resulted in an
|
||||
* unspecified error
|
||||
* - OUTPUT_INSUFFICIENT_SIZE if provided output buffer is
|
||||
* not large enough to store the resultant values
|
||||
* - OUTPUT_INSUFFICIENT_SIZE if at least one output
|
||||
* operand buffer is not large enough to store the
|
||||
* corresponding output
|
||||
* - INVALID_ARGUMENT if one of the input arguments to
|
||||
* prepareModel is invalid
|
||||
* @param outputShapes A list of shape information of model output operands.
|
||||
* The index into "outputShapes" corresponds with to index
|
||||
* of the output operand in the Request outputs vector.
|
||||
* outputShapes must be empty unless the status is either
|
||||
* NONE or OUTPUT_INSUFFICIENT_SIZE.
|
||||
*/
|
||||
oneway notify_1_2(ErrorStatus status);
|
||||
oneway notify_1_2(ErrorStatus status, vec<OutputShape> outputShapes);
|
||||
};
|
||||
|
|
|
@ -100,11 +100,17 @@ interface IPreparedModel extends @1.0::IPreparedModel {
|
|||
* - NONE if execution is performed successfully
|
||||
* - DEVICE_UNAVAILABLE if driver is offline or busy
|
||||
* - GENERAL_FAILURE if there is an unspecified error
|
||||
* - OUTPUT_INSUFFICIENT_SIZE if provided output buffer is
|
||||
* not large enough to store the resultant values
|
||||
* - OUTPUT_INSUFFICIENT_SIZE if at least one output
|
||||
* operand buffer is not large enough to store the
|
||||
* corresponding output
|
||||
* - INVALID_ARGUMENT if one of the input arguments is
|
||||
* invalid
|
||||
* @return outputShapes A list of shape information of model output operands.
|
||||
* The index into "outputShapes" corresponds to the index
|
||||
* of the output operand in the Request outputs vector.
|
||||
* outputShapes must be empty unless the status is either
|
||||
* NONE or OUTPUT_INSUFFICIENT_SIZE.
|
||||
*/
|
||||
executeSynchronously(Request request)
|
||||
generates (ErrorStatus status);
|
||||
generates (ErrorStatus status, vec<OutputShape> outputShapes);
|
||||
};
|
||||
|
|
|
@ -234,9 +234,6 @@ struct Operand {
|
|||
*
|
||||
* For a scalar operand, dimensions.size() must be 0.
|
||||
*
|
||||
* For a tensor operand, dimensions.size() must be at least 1;
|
||||
* however, any of the dimensions may be unspecified.
|
||||
*
|
||||
* A tensor operand with all dimensions specified has "fully
|
||||
* specified" dimensions. Whenever possible (i.e., whenever the
|
||||
* dimensions are known at model construction time), a tensor
|
||||
|
@ -255,17 +252,20 @@ struct Operand {
|
|||
* . The operand has lifetime CONSTANT_COPY or
|
||||
* CONSTANT_REFERENCE.
|
||||
*
|
||||
* . The operand has lifetime MODEL_INPUT or MODEL_OUTPUT. Fully
|
||||
* . The operand has lifetime MODEL_INPUT. Fully
|
||||
* specified dimensions must either be present in the
|
||||
* Operand or they must be provided in the corresponding
|
||||
* RequestArgument.
|
||||
* EXCEPTION: If the input or output is optional and omitted
|
||||
* EXCEPTION: If the input is optional and omitted
|
||||
* (by setting the hasNoValue field of the corresponding
|
||||
* RequestArgument to true) then it need not have fully
|
||||
* specified dimensions.
|
||||
*
|
||||
* A tensor operand with some number of unspecified dimensions is
|
||||
* represented by setting each unspecified dimension to 0.
|
||||
*
|
||||
* A tensor operand with unspecified rank is represented by providing
|
||||
* an empty dimensions vector.
|
||||
*/
|
||||
vec<uint32_t> dimensions;
|
||||
|
||||
|
@ -397,3 +397,18 @@ struct Model {
|
|||
*/
|
||||
bool relaxComputationFloat32toFloat16;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the shape information of an output operand after execution.
|
||||
*/
|
||||
struct OutputShape {
|
||||
/**
|
||||
* Dimensions of the operand.
|
||||
*/
|
||||
vec<uint32_t> dimensions;
|
||||
|
||||
/**
|
||||
* Whether the provided buffer size is sufficient for the output.
|
||||
*/
|
||||
bool isSufficient;
|
||||
};
|
||||
|
|
|
@ -110,15 +110,20 @@ static void validate(const sp<IPreparedModel>& preparedModel, const std::string&
|
|||
|
||||
executionCallback->wait();
|
||||
ErrorStatus executionReturnStatus = executionCallback->getStatus();
|
||||
const auto& outputShapes = executionCallback->getOutputShapes();
|
||||
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, executionReturnStatus);
|
||||
ASSERT_EQ(outputShapes.size(), 0);
|
||||
}
|
||||
|
||||
{
|
||||
SCOPED_TRACE(message + " [executeSynchronously]");
|
||||
|
||||
Return<ErrorStatus> executeStatus = preparedModel->executeSynchronously(request);
|
||||
Return<void> executeStatus = preparedModel->executeSynchronously(
|
||||
request, [](ErrorStatus error, const hidl_vec<OutputShape>& outputShapes) {
|
||||
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
|
||||
EXPECT_EQ(outputShapes.size(), 0);
|
||||
});
|
||||
ASSERT_TRUE(executeStatus.isOk());
|
||||
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(executeStatus));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue