diff options
author | Michael Butler <butlermichael@google.com> | 2018-04-13 17:26:47 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2018-04-13 17:26:47 +0000 |
commit | 984b07d42ce0ed8629a6be2229862d0db4684890 (patch) | |
tree | 4452e26bcffe8933858f4b1612a6447699d13bb7 /nn | |
parent | 665fe302d261cce3261a7bb6fa41ef519312e3f2 (diff) | |
parent | 1e9666208595bc251a8958155b1e41eca90b69db (diff) | |
download | ml-984b07d42ce0ed8629a6be2229862d0db4684890.tar.gz |
Merge changes from topic "nnapi-execution-preference" into pi-dev
* changes:
NNAPI: Add execution preference to prepareModel (runtime)
Upgrade NeuralNetworks v1.0 to v1.1 followup CL
Diffstat (limited to 'nn')
-rw-r--r-- | nn/common/CpuExecutor.cpp | 8 | ||||
-rw-r--r-- | nn/common/ValidateHal.cpp | 6 | ||||
-rw-r--r-- | nn/common/include/CpuExecutor.h | 5 | ||||
-rw-r--r-- | nn/common/include/HalInterfaces.h | 1 | ||||
-rw-r--r-- | nn/common/include/ValidateHal.h | 3 | ||||
-rw-r--r-- | nn/driver/sample/SampleDriver.cpp | 9 | ||||
-rw-r--r-- | nn/driver/sample/SampleDriver.h | 2 | ||||
-rw-r--r-- | nn/runtime/ExecutionBuilder.cpp | 9 | ||||
-rw-r--r-- | nn/runtime/ExecutionBuilder.h | 2 | ||||
-rw-r--r-- | nn/runtime/ExecutionPlan.cpp | 35 | ||||
-rw-r--r-- | nn/runtime/ExecutionPlan.h | 11 | ||||
-rw-r--r-- | nn/runtime/VersionedIDevice.cpp | 12 | ||||
-rw-r--r-- | nn/runtime/VersionedIDevice.h | 173 | ||||
-rw-r--r-- | nn/runtime/test/TestExecution.cpp | 3 | ||||
-rw-r--r-- | nn/runtime/test/TestPartitioning.cpp | 5 | ||||
-rw-r--r-- | nn/runtime/test/TestPartitioningRandom.cpp | 4 |
16 files changed, 229 insertions, 59 deletions
diff --git a/nn/common/CpuExecutor.cpp b/nn/common/CpuExecutor.cpp index 86e117d9a..333683acd 100644 --- a/nn/common/CpuExecutor.cpp +++ b/nn/common/CpuExecutor.cpp @@ -184,7 +184,13 @@ static bool setInfoAndAllocateIfNeeded(RunTimeOperandInfo* info, const Shape& sh // Ignore the .pools entry in model and request. This will have been taken care of // by the caller. -int CpuExecutor::run(const Model& model, const Request& request, +int CpuExecutor::run(const V1_0::Model& model, const Request& request, + const std::vector<RunTimePoolInfo>& modelPoolInfos, + const std::vector<RunTimePoolInfo>& requestPoolInfos) { + return run(convertToV1_1(model), request, modelPoolInfos, requestPoolInfos); +} + +int CpuExecutor::run(const V1_1::Model& model, const Request& request, const std::vector<RunTimePoolInfo>& modelPoolInfos, const std::vector<RunTimePoolInfo>& requestPoolInfos) { VLOG(CPUEXE) << "CpuExecutor::run()"; diff --git a/nn/common/ValidateHal.cpp b/nn/common/ValidateHal.cpp index e981bb3fa..095517889 100644 --- a/nn/common/ValidateHal.cpp +++ b/nn/common/ValidateHal.cpp @@ -515,5 +515,11 @@ bool validateRequest(const Request& request, const V1_1::Model& model) { return validateRequestVersioned(request, model); } +bool validateExecutionPreference(ExecutionPreference preference) { + return preference == ExecutionPreference::LOW_POWER || + preference == ExecutionPreference::FAST_SINGLE_ANSWER || + preference == ExecutionPreference::SUSTAINED_SPEED; +} + } // namespace nn } // namespace android diff --git a/nn/common/include/CpuExecutor.h b/nn/common/include/CpuExecutor.h index 23a25a9fe..c7a318bc3 100644 --- a/nn/common/include/CpuExecutor.h +++ b/nn/common/include/CpuExecutor.h @@ -106,7 +106,10 @@ public: // specified in the constructor. // The model must outlive the executor. We prevent it from being modified // while this is executing. - int run(const Model& model, const Request& request, + int run(const V1_0::Model& model, const Request& request, + const std::vector<RunTimePoolInfo>& modelPoolInfos, + const std::vector<RunTimePoolInfo>& requestPoolInfos); + int run(const V1_1::Model& model, const Request& request, const std::vector<RunTimePoolInfo>& modelPoolInfos, const std::vector<RunTimePoolInfo>& requestPoolInfos); diff --git a/nn/common/include/HalInterfaces.h b/nn/common/include/HalInterfaces.h index befede565..7467955ae 100644 --- a/nn/common/include/HalInterfaces.h +++ b/nn/common/include/HalInterfaces.h @@ -47,6 +47,7 @@ using ::android::hardware::neuralnetworks::V1_0::PerformanceInfo; using ::android::hardware::neuralnetworks::V1_0::Request; using ::android::hardware::neuralnetworks::V1_0::RequestArgument; using ::android::hardware::neuralnetworks::V1_1::Capabilities; +using ::android::hardware::neuralnetworks::V1_1::ExecutionPreference; using ::android::hardware::neuralnetworks::V1_1::IDevice; using ::android::hardware::neuralnetworks::V1_1::Model; using ::android::hardware::neuralnetworks::V1_1::Operation; diff --git a/nn/common/include/ValidateHal.h b/nn/common/include/ValidateHal.h index c3c3c5a47..d07449b71 100644 --- a/nn/common/include/ValidateHal.h +++ b/nn/common/include/ValidateHal.h @@ -38,6 +38,9 @@ bool validateModel(const V1_1::Model& model); bool validateRequest(const Request& request, const V1_0::Model& model); bool validateRequest(const Request& request, const V1_1::Model& model); +// Verfies that the execution preference is valid. +bool validateExecutionPreference(ExecutionPreference preference); + } // namespace nn } // namespace android diff --git a/nn/driver/sample/SampleDriver.cpp b/nn/driver/sample/SampleDriver.cpp index d537299df..9ebefe580 100644 --- a/nn/driver/sample/SampleDriver.cpp +++ b/nn/driver/sample/SampleDriver.cpp @@ -61,10 +61,12 @@ Return<ErrorStatus> SampleDriver::prepareModel(const V1_0::Model& model, callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr); return ErrorStatus::INVALID_ARGUMENT; } - return prepareModel_1_1(convertToV1_1(model), callback); + return prepareModel_1_1(convertToV1_1(model), ExecutionPreference::FAST_SINGLE_ANSWER, + callback); } Return<ErrorStatus> SampleDriver::prepareModel_1_1(const V1_1::Model& model, + ExecutionPreference preference, const sp<IPreparedModelCallback>& callback) { if (VLOG_IS_ON(DRIVER)) { VLOG(DRIVER) << "prepareModel_1_1"; @@ -74,7 +76,7 @@ Return<ErrorStatus> SampleDriver::prepareModel_1_1(const V1_1::Model& model, LOG(ERROR) << "invalid callback passed to prepareModel"; return ErrorStatus::INVALID_ARGUMENT; } - if (!validateModel(model)) { + if (!validateModel(model) || !validateExecutionPreference(preference)) { callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr); return ErrorStatus::INVALID_ARGUMENT; } @@ -118,8 +120,7 @@ void SamplePreparedModel::asyncExecute(const Request& request, } CpuExecutor executor; - V1_1::Model model = convertToV1_1(mModel); - int n = executor.run(model, request, mPoolInfos, requestPoolInfos); + int n = executor.run(mModel, request, mPoolInfos, requestPoolInfos); VLOG(DRIVER) << "executor.run returned " << n; ErrorStatus executionStatus = n == ANEURALNETWORKS_NO_ERROR ? ErrorStatus::NONE : ErrorStatus::GENERAL_FAILURE; diff --git a/nn/driver/sample/SampleDriver.h b/nn/driver/sample/SampleDriver.h index 89423e262..15dd8b3ce 100644 --- a/nn/driver/sample/SampleDriver.h +++ b/nn/driver/sample/SampleDriver.h @@ -41,7 +41,7 @@ public: getSupportedOperations_cb cb) override; Return<ErrorStatus> prepareModel(const V1_0::Model& model, const sp<IPreparedModelCallback>& callback) override; - Return<ErrorStatus> prepareModel_1_1(const V1_1::Model& model, + Return<ErrorStatus> prepareModel_1_1(const V1_1::Model& model, ExecutionPreference preference, const sp<IPreparedModelCallback>& callback) override; Return<DeviceStatus> getStatus() override; diff --git a/nn/runtime/ExecutionBuilder.cpp b/nn/runtime/ExecutionBuilder.cpp index 02efd408b..63301b10a 100644 --- a/nn/runtime/ExecutionBuilder.cpp +++ b/nn/runtime/ExecutionBuilder.cpp @@ -552,7 +552,14 @@ int StepExecutor::startComputeOnDevice(sp<ExecutionCallback>* synchronizationCal // TODO Dangerous! In async, the model will outlive it here. Safe for now sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback(); - ErrorStatus prepareLaunchStatus = mDriver->prepareModel(model, preparedModelCallback); + // TODO(butlermichael): Propagate user preference to this point instead of + // using default value of ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER, or + // remove this entire block of code since it is a stale path that is only + // encountered on an #if-removed code. + ExecutionPreference preference = + static_cast<ExecutionPreference>(ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER); + ErrorStatus prepareLaunchStatus = mDriver->prepareModel(model, preference, + preparedModelCallback); if (prepareLaunchStatus != ErrorStatus::NONE) { return convertErrorStatusToResultCode(prepareLaunchStatus); } diff --git a/nn/runtime/ExecutionBuilder.h b/nn/runtime/ExecutionBuilder.h index 40b41e920..7c188d3de 100644 --- a/nn/runtime/ExecutionBuilder.h +++ b/nn/runtime/ExecutionBuilder.h @@ -22,7 +22,6 @@ #include "Memory.h" #include "ModelBuilder.h" #include "NeuralNetworks.h" -#include "VersionedIDevice.h" #include <unordered_map> #include <vector> @@ -38,6 +37,7 @@ class ExecutionPlan; class Memory; class ModelBuilder; class StepExecutor; +class VersionedIDevice; // TODO move length out of DataLocation struct ModelArgumentInfo { diff --git a/nn/runtime/ExecutionPlan.cpp b/nn/runtime/ExecutionPlan.cpp index 655d51266..10470ba39 100644 --- a/nn/runtime/ExecutionPlan.cpp +++ b/nn/runtime/ExecutionPlan.cpp @@ -38,17 +38,16 @@ using ::android::hardware::neuralnetworks::V1_0::implementation::PreparedModelCa namespace android { namespace nn { -static int compile(std::shared_ptr<Device> device, - const ModelBuilder* model, - sp<IPreparedModel>* preparedModel) { +static int compile(std::shared_ptr<Device> device, const ModelBuilder* model, + int32_t executionPreference, sp<IPreparedModel>* preparedModel) { nnAssert(device != nullptr); // nullptr indicates CPU // Compilation logic copied from ExecutionBuilder::startComputeOnDevice(). Model hidlModel; model->setHidlModel(&hidlModel); sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback(); - Return<ErrorStatus> prepareLaunchStatus = - device->getInterface()->prepareModel(hidlModel, preparedModelCallback); + Return<ErrorStatus> prepareLaunchStatus = device->getInterface()->prepareModel( + hidlModel, static_cast<ExecutionPreference>(executionPreference), preparedModelCallback); if (!prepareLaunchStatus.isOk()) { LOG(ERROR) << "ExecutionStep::finishSubModel compilation failed due to transport error: " << prepareLaunchStatus.description(); @@ -347,7 +346,8 @@ static void convertModelInputsOrOutputs( } } -int ExecutionStep::finishSubModel(const ModelBuilder* fromModel, bool* hasOutputOfUnknownSize) { +int ExecutionStep::finishSubModel(const ModelBuilder* fromModel, bool* hasOutputOfUnknownSize, + int32_t executionPreference) { if (VLOG_IS_ON(COMPILATION)) { logSubModel(); } @@ -430,7 +430,7 @@ int ExecutionStep::finishSubModel(const ModelBuilder* fromModel, bool* hasOutput } VLOG(COMPILATION) << "ExecutionStep::finishSubModel, compilation"; - return compile(mDevice, &mSubModel, &mPreparedSubModel); + return compile(mDevice, &mSubModel, executionPreference, &mPreparedSubModel); } void ExecutionStep::dump() const { @@ -443,10 +443,12 @@ void ExecutionStep::dump() const { } } -int ExecutionPlan::CompoundBody::finish(const ModelBuilder* fromModel) { +int ExecutionPlan::CompoundBody::finish(const ModelBuilder* fromModel, + int32_t executionPreference) { findTempsAsSubModelOutputs(); for (const auto& step : mSteps) { - int n = step->finishSubModel(fromModel, &mHasSubModelOutputOfUnknownSize); + int n = step->finishSubModel(fromModel, &mHasSubModelOutputOfUnknownSize, + executionPreference); if (n != ANEURALNETWORKS_NO_ERROR) { VLOG(COMPILATION) << "ExecutionPlan::CompoundBody::finish -- finishSubModel failed"; return n; @@ -461,21 +463,22 @@ int ExecutionPlan::CompoundBody::finish(const ModelBuilder* fromModel) { return ANEURALNETWORKS_NO_ERROR; } -int ExecutionPlan::SimpleBody::finish([[maybe_unused]] const ModelBuilder* fromModel) { +int ExecutionPlan::SimpleBody::finish([[maybe_unused]] const ModelBuilder* fromModel, + int32_t executionPreference) { if (mDevice == nullptr) { mSuccessfulFinish = true; return ANEURALNETWORKS_NO_ERROR; } VLOG(COMPILATION) << "ExecutionPlan::SimpleBody::finish, compilation"; - const int n = compile(mDevice, mModel, &mPreparedModel); + const int n = compile(mDevice, mModel, executionPreference, &mPreparedModel); mSuccessfulFinish = (n == ANEURALNETWORKS_NO_ERROR); return n; } -int ExecutionPlan::finish(const ModelBuilder* fromModel) { +int ExecutionPlan::finish(const ModelBuilder* fromModel, int32_t executionPreference) { nnAssert(mBody != nullptr); - return mBody->finish(fromModel); + return mBody->finish(fromModel, executionPreference); } ExecutionPlan::Controller::Controller( @@ -779,7 +782,7 @@ int ModelBuilder::partitionTheWork(const std::vector<std::shared_ptr<Device>>& d } } plan->becomeSingleStep(nullptr /* CPU */, this); - return plan->finish(this); + return plan->finish(this, preference); } // Figure out where each operation will best execute. @@ -801,7 +804,7 @@ int ModelBuilder::partitionTheWork(const std::vector<std::shared_ptr<Device>>& d << bestDeviceIndex << " = " << (cpu ? "CPU" : devices[bestDeviceIndex]->getName()); plan->becomeSingleStep(cpu ? nullptr : devices[bestDeviceIndex], this); - return plan->finish(this); + return plan->finish(this, preference); } // No easy solution, we need to split the work. @@ -860,7 +863,7 @@ int ModelBuilder::partitionTheWork(const std::vector<std::shared_ptr<Device>>& d } } - int n = plan->finish(this); + int n = plan->finish(this, preference); if (VLOG_IS_ON(COMPILATION)) { Model model; setHidlModel(&model); diff --git a/nn/runtime/ExecutionPlan.h b/nn/runtime/ExecutionPlan.h index 29467575d..843447275 100644 --- a/nn/runtime/ExecutionPlan.h +++ b/nn/runtime/ExecutionPlan.h @@ -80,7 +80,8 @@ public: // If this step has a submodel output of unknown size, sets // *hasOutputOfUnknownSize to true; otherwise, leaves it // unchanged. - int finishSubModel(const ModelBuilder* fromModel, bool* hasOutputOfUnknownSize); + int finishSubModel(const ModelBuilder* fromModel, bool* hasOutputOfUnknownSize, + int32_t executionPreference); const ModelBuilder* getSubModel() const { return &mSubModel; } std::shared_ptr<Device> getDevice() const { return mDevice; } @@ -203,7 +204,7 @@ public: void becomeSingleStep(const std::shared_ptr<Device> device, const ModelBuilder* model); - int finish(const ModelBuilder* fromModel); + int finish(const ModelBuilder* fromModel, int32_t executionPreference); void recordTemporaryDef(uint32_t fromModelIndex, uint32_t stepIndex) { auto& temporaryToDefiningStep = compound()->mTemporaryToDefiningStep; @@ -226,7 +227,7 @@ private: struct Body { virtual ~Body() {} virtual void dump() const = 0; - virtual int finish(const ModelBuilder* fromModel) = 0; + virtual int finish(const ModelBuilder* fromModel, int32_t executionPreference) = 0; bool mSuccessfulFinish = false; }; @@ -235,7 +236,7 @@ private: mDevice(device), mModel(model) {} void dump() const override; - int finish(const ModelBuilder* fromModel) override; + int finish(const ModelBuilder* fromModel, int32_t executionPreference) override; std::shared_ptr<Device> mDevice; // nullptr signifies CPU const ModelBuilder* mModel; @@ -244,7 +245,7 @@ private: struct CompoundBody : Body { void dump() const override; - int finish(const ModelBuilder* fromModel) override; + int finish(const ModelBuilder* fromModel, int32_t executionPreference) override; // TODO: Some of the data is working state information that // shouldn't be needed after we've constructed but not diff --git a/nn/runtime/VersionedIDevice.cpp b/nn/runtime/VersionedIDevice.cpp index 5cd78424a..110eecc47 100644 --- a/nn/runtime/VersionedIDevice.cpp +++ b/nn/runtime/VersionedIDevice.cpp @@ -33,7 +33,7 @@ std::pair<ErrorStatus, Capabilities> VersionedIDevice::getCapabilities() { if (mDeviceV1_1 != nullptr) { Return<void> ret = mDeviceV1_1->getCapabilities_1_1( - [&](ErrorStatus error, const Capabilities& capabilities) { + [&result](ErrorStatus error, const Capabilities& capabilities) { result = std::make_pair(error, capabilities); }); if (!ret.isOk()) { @@ -42,7 +42,7 @@ std::pair<ErrorStatus, Capabilities> VersionedIDevice::getCapabilities() { } } else if (mDeviceV1_0 != nullptr) { Return<void> ret = mDeviceV1_0->getCapabilities( - [&](ErrorStatus error, const V1_0::Capabilities& capabilities) { + [&result](ErrorStatus error, const V1_0::Capabilities& capabilities) { result = std::make_pair(error, convertToV1_1(capabilities)); }); if (!ret.isOk()) { @@ -63,7 +63,7 @@ std::pair<ErrorStatus, hidl_vec<bool>> VersionedIDevice::getSupportedOperations( if (mDeviceV1_1 != nullptr) { Return<void> ret = mDeviceV1_1->getSupportedOperations_1_1( - model, [&](ErrorStatus error, const hidl_vec<bool>& supported) { + model, [&result](ErrorStatus error, const hidl_vec<bool>& supported) { result = std::make_pair(error, supported); }); if (!ret.isOk()) { @@ -72,7 +72,7 @@ std::pair<ErrorStatus, hidl_vec<bool>> VersionedIDevice::getSupportedOperations( } } else if (mDeviceV1_0 != nullptr && compliantWithV1_0(model)) { Return<void> ret = mDeviceV1_0->getSupportedOperations( - convertToV1_0(model), [&](ErrorStatus error, const hidl_vec<bool>& supported) { + convertToV1_0(model), [&result](ErrorStatus error, const hidl_vec<bool>& supported) { result = std::make_pair(error, supported); }); if (!ret.isOk()) { @@ -89,10 +89,10 @@ std::pair<ErrorStatus, hidl_vec<bool>> VersionedIDevice::getSupportedOperations( return result; } -ErrorStatus VersionedIDevice::prepareModel(const Model& model, +ErrorStatus VersionedIDevice::prepareModel(const Model& model, ExecutionPreference preference, const sp<IPreparedModelCallback>& callback) { if (mDeviceV1_1 != nullptr) { - Return<ErrorStatus> ret = mDeviceV1_1->prepareModel_1_1(model, callback); + Return<ErrorStatus> ret = mDeviceV1_1->prepareModel_1_1(model, preference, callback); if (!ret.isOk()) { LOG(ERROR) << "prepareModel_1_1 failure: " << ret.description(); return ErrorStatus::GENERAL_FAILURE; diff --git a/nn/runtime/VersionedIDevice.h b/nn/runtime/VersionedIDevice.h index f2b308818..561d780ce 100644 --- a/nn/runtime/VersionedIDevice.h +++ b/nn/runtime/VersionedIDevice.h @@ -26,39 +26,174 @@ namespace android { namespace nn { -// TODO(butlermichael): document VersionedIDevice class +/** + * This object is either a V1_1::IDevice or V1_0::IDevice object. This class + * abstracts away version differences, allowing the remainder of the runtime to + * always use the most up-to-date version of all HIDL types. As such, any + * reference to a HIDL type in the rest of the runtime will--by default--be the + * latest HIDL version. + * + * This class will attempt to call the latest version of each interface method + * if possible. If the latest method is unavailable, the VersionedIDevice class + * will attempt to upcast the type (e.g., V1_1::Model to V1_0::Model), and + * invoke the latest interface method possible. If the VersionedIDevice class + * fails to find a matching applicable function, it will return an error. + */ class VersionedIDevice { DISALLOW_IMPLICIT_CONSTRUCTORS(VersionedIDevice); public: + /** + * Constructor for the VersionedIDevice object. + * + * VersionedIDevice is constructed with the V1_0::IDevice object, which + * represents a device that is at least v1.0 of the interface. The + * constructor downcasts to the latest version of the IDevice interface, and + * will default to using the latest version of all IDevice interface + * methods automatically. + * + * @param device A device object that is least version 1.0 of the IDevice + * interface. + */ VersionedIDevice(sp<V1_0::IDevice> device); + /** + * Gets the capabilities of a driver. + * + * @return status Error status of the call, must be: + * - NONE if successful + * - DEVICE_UNAVAILABLE if driver is offline or busy + * - GENERAL_FAILURE if there is an unspecified error + * @return capabilities Capabilities of the driver. + */ std::pair<ErrorStatus, Capabilities> getCapabilities(); - ErrorStatus prepareModel(const Model& model, const sp<IPreparedModelCallback>& callback); - + /** + * Gets the supported operations in a model. + * + * getSupportedSubgraph indicates which operations of a model are fully + * supported by the vendor driver. If an operation may not be supported for + * any reason, getSupportedOperations must return false for that operation. + * + * @param model A model whose operations--and their corresponding + * operands--are to be verified by the driver. + * @return status Error status of the call, must be: + * - NONE if successful + * - DEVICE_UNAVAILABLE if driver is offline or busy + * - GENERAL_FAILURE if there is an unspecified error + * - INVALID_ARGUMENT if provided model is invalid + * @return supportedOperations A list of supported operations, where true + * indicates the operation is supported and + * false indicates the operation is not + * supported. The index of "supported" + * corresponds with the index of the operation + * it is describing. + */ std::pair<ErrorStatus, hidl_vec<bool>> getSupportedOperations(const Model& model); + /** + * Creates a prepared model for execution. + * + * prepareModel is used to make any necessary transformations or alternative + * representations to a model for execution, possiblly including + * transformations on the constant data, optimization on the model's graph, + * or compilation into the device's native binary format. The model itself + * is not changed. + * + * The model is prepared asynchronously with respect to the caller. The + * prepareModel function must verify the inputs to the prepareModel function + * are correct. If there is an error, prepareModel must immediately invoke + * the callback with the appropriate ErrorStatus value and nullptr for the + * IPreparedModel, then return with the same ErrorStatus. If the inputs to + * the prepareModel function are valid and there is no error, prepareModel + * must launch an asynchronous task to prepare the model in the background, + * and immediately return from prepareModel with ErrorStatus::NONE. If the + * asynchronous task fails to launch, prepareModel must immediately invoke + * the callback with ErrorStatus::GENERAL_FAILURE and nullptr for the + * IPreparedModel, then return with ErrorStatus::GENERAL_FAILURE. + * + * When the asynchronous task has finished preparing the model, it must + * immediately invoke the callback function provided as an input to + * prepareModel. If the model was prepared successfully, the callback object + * must be invoked with an error status of ErrorStatus::NONE and the + * produced IPreparedModel object. If an error occurred preparing the model, + * the callback object must be invoked with the appropriate ErrorStatus + * value and nullptr for the IPreparedModel. + * + * The only information that may be unknown to the model at this stage is + * the shape of the tensors, which may only be known at execution time. As + * such, some driver services may return partially prepared models, where + * the prepared model can only be finished when it is paired with a set of + * inputs to the model. Note that the same prepared model object can be + * used with different shapes of inputs on different (possibly concurrent) + * executions. + * + * Multiple threads can call prepareModel on the same model concurrently. + * + * @param model The model to be prepared for execution. + * @param callback A callback object used to return the error status of + * preparing the model for execution and the prepared model + * if successful, nullptr otherwise. The callback object's + * notify function must be called exactly once, even if the + * model could not be prepared. + * @return status Error status of launching a task which prepares the model + * in the background; must be: + * - NONE if preparation 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 + */ + ErrorStatus prepareModel(const Model& model, ExecutionPreference preference, + const sp<IPreparedModelCallback>& callback); + + /** + * Returns the current status of a driver. + * + * @return status Status of the driver, one of: + * - DeviceStatus::AVAILABLE + * - DeviceStatus::BUSY + * - DeviceStatus::OFFLINE + * - DeviceStatus::UNKNOWN + */ DeviceStatus getStatus(); - bool operator==(nullptr_t); + /** + * Returns whether this handle to an IDevice object is valid or not. + * + * @return bool true if V1_0::IDevice (which could be V1_1::IDevice) is + * valid, false otherwise. + */ bool operator!=(nullptr_t); + /** + * Returns whether this handle to an IDevice object is valid or not. + * + * @return bool true if V1_0::IDevice (which could be V1_1::IDevice) is + * invalid, false otherwise. + */ + bool operator==(nullptr_t); + private: - // Both versions of IDevice are necessary because the driver could v1.0, - // v1.1, or a later version. These two pointers logically represent the same - // object. - // - // The general strategy is: HIDL returns a V1_0 device object, which - // (if not nullptr) could be v1.0, v1.1, or a greater version. The V1_0 - // object is then "dynamically cast" to a V1_1 object. If successful, - // mDeviceV1_1 will point to the same object as mDeviceV1_0; otherwise, - // mDeviceV1_1 will be nullptr. - // - // In general: - // * If the device is truly v1.0, mDeviceV1_0 will point to a valid object - // and mDeviceV1_1 will be nullptr. - // * If the device is truly v1.1 or later, both mDeviceV1_0 and mDeviceV1_1 - // will point to the same valid object. + /** + * Both versions of IDevice are necessary because the driver could be v1.0, + * v1.1, or a later version. These two pointers logically represent the same + * object. + * + * The general strategy is: HIDL returns a V1_0 device object, which + * (if not nullptr) could be v1.0, v1.1, or a greater version. The V1_0 + * object is then "dynamically cast" to a V1_1 object. If successful, + * mDeviceV1_1 will point to the same object as mDeviceV1_0; otherwise, + * mDeviceV1_1 will be nullptr. + * + * In general: + * * If the device is truly v1.0, mDeviceV1_0 will point to a valid object + * and mDeviceV1_1 will be nullptr. + * * If the device is truly v1.1 or later, both mDeviceV1_0 and mDeviceV1_1 + * will point to the same valid object. + * + * Idiomatic usage: if mDeviceV1_1 is non-null, do V1_1 dispatch; otherwise, + * do V1_0 dispatch. + */ sp<V1_0::IDevice> mDeviceV1_0; sp<V1_1::IDevice> mDeviceV1_1; }; diff --git a/nn/runtime/test/TestExecution.cpp b/nn/runtime/test/TestExecution.cpp index 26e1dc379..8df7464e6 100644 --- a/nn/runtime/test/TestExecution.cpp +++ b/nn/runtime/test/TestExecution.cpp @@ -109,11 +109,12 @@ public: Return<ErrorStatus> prepareModel_1_1( const HidlModel& model, + ExecutionPreference preference, const sp<IPreparedModelCallback>& actualCallback) override { sp<PreparedModelCallback> localCallback = new PreparedModelCallback; Return<ErrorStatus> prepareModelReturn = - SampleDriver::prepareModel_1_1(model, localCallback); + SampleDriver::prepareModel_1_1(model, preference, localCallback); if (!prepareModelReturn.isOkUnchecked()) { return prepareModelReturn; } diff --git a/nn/runtime/test/TestPartitioning.cpp b/nn/runtime/test/TestPartitioning.cpp index 690fff872..9b989b716 100644 --- a/nn/runtime/test/TestPartitioning.cpp +++ b/nn/runtime/test/TestPartitioning.cpp @@ -242,11 +242,12 @@ public: mOperationMask(operationMask), mOEM(oem) {} ~PartitioningDriver() override {} - Return<ErrorStatus> prepareModel_1_1(const Model&, + Return<ErrorStatus> prepareModel_1_1(const Model&, ExecutionPreference, const sp<IPreparedModelCallback>& cb) override { cb->notify(ErrorStatus::NONE, new PartitioningPreparedModel); return ErrorStatus::NONE; } + Return<DeviceStatus> getStatus() override { return DeviceStatus::AVAILABLE; } @@ -255,6 +256,7 @@ public: cb(ErrorStatus::NONE, mCapabilities); return Void(); } + Return<void> getSupportedOperations_1_1(const Model& model, getSupportedOperations_cb cb) override { if (!android::nn::validateModel(model)) { @@ -278,6 +280,7 @@ public: cb(ErrorStatus::NONE, supported); return Void(); } + private: Capabilities mCapabilities; uint32_t mOperationMask; diff --git a/nn/runtime/test/TestPartitioningRandom.cpp b/nn/runtime/test/TestPartitioningRandom.cpp index a72935dad..c302b133f 100644 --- a/nn/runtime/test/TestPartitioningRandom.cpp +++ b/nn/runtime/test/TestPartitioningRandom.cpp @@ -510,7 +510,7 @@ public: return Void(); } - Return<ErrorStatus> prepareModel_1_1(const HidlModel& model, + Return<ErrorStatus> prepareModel_1_1(const HidlModel& model, ExecutionPreference preference, const sp<IPreparedModelCallback>& callback) override { // NOTE: We verify that all operations in the model are supported. ErrorStatus outStatus = ErrorStatus::INVALID_ARGUMENT; @@ -525,7 +525,7 @@ public: } }); if (ret.isOk() && (outStatus == ErrorStatus::NONE)) { - return SampleDriver::prepareModel_1_1(model, callback); + return SampleDriver::prepareModel_1_1(model, preference, callback); } else { callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr); return ErrorStatus::INVALID_ARGUMENT; |