Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b50bc313 authored by Xusong Wang's avatar Xusong Wang Committed by Miao Wang
Browse files

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
Merged-In: I01108913212d9f4aa47daf2f293ea19259925865
(cherry picked from commit 187c5971)
parent 1cc4ee85
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -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
+58 −11
Original line number Diff line number Diff line
@@ -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();

    /**
     * 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;
};


+28 −9
Original line number Diff line number Diff line
@@ -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);
+10 −3
Original line number Diff line number Diff line
@@ -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);
};
+7 −2
Original line number Diff line number Diff line
@@ -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));
    }
}