| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef ANDROID_FRAMEWORKS_ML_NN_RUNTIME_VERSIONED_INTERFACES_H |
| #define ANDROID_FRAMEWORKS_ML_NN_RUNTIME_VERSIONED_INTERFACES_H |
| |
| #include <android-base/macros.h> |
| |
| #include <cstddef> |
| #include <functional> |
| #include <memory> |
| #include <optional> |
| #include <shared_mutex> |
| #include <string> |
| #include <tuple> |
| #include <utility> |
| #include <vector> |
| |
| #include "Callbacks.h" |
| #include "HalInterfaces.h" |
| #include "Utils.h" |
| |
| namespace android { |
| namespace nn { |
| |
| // forward declarations |
| class ExecutionBurstController; |
| class IDeviceDeathHandler; |
| class IPreparedModelDeathHandler; |
| class MetaModel; |
| class VersionedIPreparedModel; |
| |
| /** |
| * Each class (VersionedIDevice, VersionedIPreparedModel) wraps a HIDL interface |
| * of any version to abstract away version differences. It allows 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. |
| * |
| * Each class will attempt to call the latest version of each interface method |
| * if possible. If the latest method is unavailable, the versioned 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 versioned class |
| * fails to find a matching applicable function, it will return an error. |
| */ |
| |
| /** This class wraps an IDevice object of any version. */ |
| class VersionedIDevice { |
| DISALLOW_IMPLICIT_CONSTRUCTORS(VersionedIDevice); |
| |
| // forward declaration of nested class |
| class Core; |
| |
| public: |
| /** |
| * Create a VersionedIDevice object. |
| * |
| * Prefer using this function over the constructor, as it adds more |
| * protections. |
| * |
| * @param serviceName The name of the service that provides "device". |
| * @param makeDevice A device factory function that returns a device object |
| * that is at least version 1.0 of the IDevice interface. |
| * @return A valid VersionedIDevice object, otherwise nullptr. |
| */ |
| static std::shared_ptr<VersionedIDevice> create(std::string serviceName, |
| const hal::DeviceFactory& makeDevice); |
| |
| /** |
| * Constructor for the VersionedIDevice object. |
| * |
| * VersionedIDevice will default to using the latest version of all IDevice |
| * interface methods automatically. |
| * |
| * @param capabilities Performance capabilities of the driver. |
| * @param supportedExtensions Extensions supported by the driver. |
| * @param type The device type of the driver. |
| * @param versionString The version string of the driver. |
| * @param numberOfCacheFilesNeeded Number of model cache and data cache |
| * files needed by the driver. |
| * @param serviceName The name of the service that provides core.getDevice<V1_0::IDevice>(). |
| * @param makeDevice A device factory function that returns a device object |
| * that is at least version 1.0 of the IDevice interface. |
| * @param core An object that encapsulates a V1_0::IDevice, any appropriate downcasts to |
| * newer interfaces, and a hidl_death_recipient that will proactively handle |
| * the case when the service containing the IDevice object crashes. |
| */ |
| VersionedIDevice(hal::Capabilities capabilities, |
| std::vector<hal::Extension> supportedExtensions, int32_t type, |
| std::string versionString, |
| std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded, |
| std::string serviceName, const hal::DeviceFactory& makeDevice, Core core); |
| |
| /** |
| * Gets the capabilities of a driver. |
| * |
| * @return capabilities Capabilities of the driver. |
| */ |
| const hal::Capabilities& getCapabilities() const; |
| |
| /** |
| * Gets information about extensions supported by the driver implementation. |
| * |
| * Extensions of category ExtensionCategory::BASE must not appear |
| * in the list. |
| * |
| * All extension operations and operands must be fully supported for the |
| * extension to appear in the list of supported extensions. |
| * |
| * @return extensions A list of supported extensions. |
| */ |
| const std::vector<hal::Extension>& getSupportedExtensions() const; |
| |
| /** |
| * Gets the supported operations in a MetaModel. |
| * |
| * getSupportedOperations indicates which operations of |
| * MetaModel::getModel() 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 metaModel A MetaModel whose operations--and their corresponding |
| * operands--are to be verified by the driver. When |
| * metaModel.getModel() is not compliant with the HAL |
| * version of the vendor driver, the MetaModel's slicing |
| * functionality (MetaModel::getSlice*()) is employed |
| * to query the vendor driver about which of the subset of |
| * compliant operations are supported. See the MetaModel |
| * class in MetaModel.h for more details. |
| * @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<hal::ErrorStatus, hal::hidl_vec<bool>> getSupportedOperations( |
| const MetaModel& metaModel) const; |
| |
| /** |
| * Creates a prepared model for execution. |
| * |
| * prepareModel is used to make any necessary transformations or alternative |
| * representations to a model for execution, possibly 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. |
| * |
| * Optionally, caching information may be provided for the driver to either: |
| * - load the prepared model from cache, bypassing full model preparation |
| * - save the prepared model to cache for faster model compilation time when |
| * the same model preparation is requested in the future |
| * |
| * The prepareModel function must verify the inputs to the prepareModel |
| * function are correct. If there is an error, prepareModel must immediately |
| * return the appropriate result code and nullptr for the |
| * VersionedIPreparedModel. If the inputs to the prepareModel function are |
| * valid and there is no error, prepareModel must prepare the model. |
| * |
| * If the model was prepared successfully, prepareModel must return |
| * ANEURALNETWORKS_NO_ERROR and the produced VersionedIPreparedModel object. |
| * If an error occurred preparing the model, prepareModel must return the |
| * appropriate result code and nullptr for the VersionedIPreparedModel. |
| * |
| * 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 may only be finished when it is paired with a set of |
| * inputs to the model. Note that the same prepared model object may be |
| * used with different shapes of inputs on different (possibly concurrent) |
| * executions. |
| * |
| * Multiple threads may call prepareModel on the same model concurrently. |
| * |
| * @param makeModel Factory function to create the model to be prepared for |
| * execution. |
| * @param preference Indicates the intended execution behavior of a prepared |
| * model. |
| * @param priority Priority of the prepared model relative to other prepared |
| * models owned by an application. |
| * @param deadline Optional time point. If provided, prepareModel is |
| * expected to complete by this time point. If it is not able to be |
| * completed by the deadline, the execution may be aborted. |
| * @param cacheDir String specifying the cache directory. |
| * @param maybeToken An optional caching token of length |
| * Constant::BYTE_SIZE_OF_CACHE_TOKEN identifying the prepared model. |
| * The same token will be provided when retrieving the prepared model |
| * from the cache files with prepareModelFromCache. Tokens should be |
| * chosen to have a low rate of collision for a particular application. |
| * The driver cannot detect a collision; a collision will result in a |
| * failed execution or in a successful execution that produces incorrect |
| * output values. If both modelCache and dataCache are empty indicating |
| * that caching information is not provided, this token must be ignored. |
| * @return A pair of: |
| * - Result code of preparing the model; must be: |
| * - ANEURALNETWORKS_NO_ERROR if preparation succeeded |
| * - ANEURALNETWORKS_UNAVAILABLE_DEVICE if driver is offline or busy |
| * - ANEURALNETWORKS_OP_FAILED if there is an unspecified error |
| * - ANEURALNETWORKS_BAD_DATA if one of the input arguments related |
| * to preparing the model is invalid |
| * - preparedModel A VersionedIPreparedModel object representing a model |
| * that has been prepared for execution, else nullptr. |
| */ |
| std::pair<int, std::shared_ptr<VersionedIPreparedModel>> prepareModel( |
| const hal::ModelFactory& makeModel, hal::ExecutionPreference preference, hal::Priority, |
| const std::optional<Deadline>& deadline, const std::string& cacheDir, |
| const std::optional<hal::CacheToken>& maybeToken) const; |
| |
| /** |
| * Returns the feature level of a driver. |
| * |
| * @return featureLevel The API level of the most advanced feature this driver implements. |
| * For example, if the driver implements the features introduced in |
| * Android P, the value would be 28. |
| * Return -1 if the driver is offline or busy, or the query resulted in |
| * an unspecified error. |
| */ |
| int64_t getFeatureLevel() const; |
| |
| /** |
| * Returns the device type of a driver. |
| * |
| * @return deviceType The type of a given device, which can help application |
| * developers to distribute Machine Learning workloads and other |
| * workloads such as graphical rendering. E.g., for an app which renders |
| * AR scenes based on real time object detection results, the developer |
| * could choose an ACCELERATOR type device for ML workloads, and reserve |
| * GPU for graphical rendering. |
| */ |
| int32_t getType() const; |
| |
| /** |
| * Get the version string of the driver implementation. |
| * |
| * The version string must be a unique token among the set of version strings of |
| * drivers of a specific device. The token identifies the device driver's |
| * implementation. The token must not be confused with the feature level which is solely |
| * defined by the interface version. This API is opaque to the Android framework, but the |
| * Android framework may use the information for debugging or to pass on to NNAPI applications. |
| * |
| * Application developers sometimes have specific requirements to ensure good user experiences, |
| * and they need more information to make intelligent decisions when the Android framework |
| * cannot. For example, combined with the device name and other information, the token can help |
| * NNAPI applications filter devices based on their needs: |
| * - An application demands a certain level of performance, but a specific version of |
| * the driver cannot meet that requirement because of a performance regression. |
| * The application can blacklist the driver based on the version provided. |
| * - An application has a minimum precision requirement, but certain versions of |
| * the driver cannot meet that requirement because of bugs or certain optimizations. |
| * The application can filter out versions of these drivers. |
| * |
| * @return version The version string of the device implementation. |
| */ |
| const std::string& getVersionString() const; |
| |
| /** |
| * Gets the caching requirements of the driver implementation. |
| * |
| * There are two types of cache file descriptors provided to the driver: model cache |
| * and data cache. |
| * |
| * The data cache is for caching constant data, possibly including preprocessed |
| * and transformed tensor buffers. Any modification to the data cache should |
| * have no worse effect than generating bad output values at execution time. |
| * |
| * The model cache is for caching security-sensitive data such as compiled |
| * executable machine code in the device's native binary format. A modification |
| * to the model cache may affect the driver's execution behavior, and a malicious |
| * client could make use of this to execute beyond the granted permission. Thus, |
| * the driver must always check whether the model cache is corrupted before |
| * preparing the model from cache. |
| * |
| * getNumberOfCacheFilesNeeded returns how many of each type of cache files the driver |
| * implementation needs to cache a single prepared model. Returning 0 for both types |
| * indicates compilation caching is not supported by this driver. The driver may |
| * still choose not to cache certain compiled models even if it reports that caching |
| * is supported. |
| * |
| * If the device reports that caching is not supported, the user may avoid calling |
| * IDevice::prepareModelFromCache or providing cache file descriptors to |
| * IDevice::prepareModel_1_2. |
| * |
| * @return numModelCache An unsigned integer indicating how many files for model cache |
| * the driver needs to cache a single prepared model. It must |
| * be less than or equal to Constant::MAX_NUMBER_OF_CACHE_FILES. |
| * @return numDataCache An unsigned integer indicating how many files for data cache |
| * the driver needs to cache a single prepared model. It must |
| * be less than or equal to Constant::MAX_NUMBER_OF_CACHE_FILES. |
| */ |
| std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const; |
| |
| /** |
| * Returns the name of the service. |
| * |
| * @return Name of the service. |
| */ |
| const std::string& getName() const; |
| |
| /** |
| * Allocates a driver-managed buffer with the properties specified by the descriptor as well as |
| * the input and output roles of prepared models. |
| * |
| * The allocate function must verify the inputs to the allocate function are correct. If there |
| * is an error, or if a certain role or property is not supported by the driver, the allocate |
| * function must return with an appropriate ErrorStatus, a nullptr as the IBuffer, and 0 as the |
| * buffer token. If the allocation is successful, this method must return with ErrorStatus::NONE |
| * and the produced IBuffer with a positive token identifying the allocated buffer. A successful |
| * allocation must accommodate all of the specified roles and buffer properties. |
| * |
| * The buffer is allocated as an uninitialized state. An uninitialized buffer may only be used |
| * in ways that are specified by outputRoles. A buffer is initialized after it is used as an |
| * output in a successful execution, or after a successful invocation of IBuffer::copyFrom on |
| * the buffer. An initialized buffer may be used according to all roles specified in inputRoles |
| * and outputRoles. A buffer will return to the uninitialized state if it is used as an output |
| * in a failed execution, or after a failed invocation of IBuffer::copyFrom on the buffer. |
| * |
| * The driver may deduce the dimensions of the buffer according to the buffer descriptor as |
| * well as the input and output roles. The dimensions or rank of the buffer may be unknown at |
| * this stage. As such, some driver services may only create a placeholder and defer the actual |
| * allocation until execution time. Note that the same buffer may be used for different shapes |
| * of outputs on different executions. When the buffer is used as an input, the input shape |
| * must be the same as the output shape from the last execution using this buffer as an output. |
| * |
| * The driver must apply proper validatation upon every usage of the buffer, and fail the |
| * execution immediately if the usage is illegal. |
| * |
| * @param desc A buffer descriptor specifying the properties of the buffer to allocate. |
| * @param preparedModels A vector of IPreparedModel objects. Must only contain IPreparedModel |
| * objects from the same IDevice as this method invoked on. |
| * @param inputRoles A vector of roles with each specifying an input to a prepared model. |
| * @param outputRoles A vector of roles with each specifying an output to a prepared model. |
| * Each role specified in inputRoles and outputRoles must be unique. The corresponding |
| * model operands of the roles must have the same OperandType, scale, zero point, and |
| * ExtraParams. The dimensions of the operands and the dimensions specified in the buffer |
| * descriptor must be compatible with each other. Two dimensions are incompatible if there |
| * is at least one axis that is fully specified in both but has different values. |
| * @return A tuple consisting of: |
| * - Error status of the buffer allocation. Must be: |
| * - NONE if successful |
| * - DEVICE_UNAVAILABLE if driver is offline or busy |
| * - GENERAL_FAILURE if a certain buffer property or a certain role is not supported, |
| * or if there is an unspecified error |
| * - INVALID_ARGUMENT if one of the input arguments is invalid |
| * - The allocated IBuffer object. If the buffer was unable to be allocated |
| * due to an error, nullptr must be returned. |
| * - A positive token identifying the allocated buffer. The same token will be |
| * provided when referencing the buffer as one of the memory pools in the request of an |
| * execution. If the buffer was unable to be allocated due to an error, the token must be |
| * 0. |
| */ |
| std::tuple<hal::ErrorStatus, sp<hal::IBuffer>, uint32_t> allocate( |
| const hal::BufferDesc& desc, |
| const std::vector<std::shared_ptr<VersionedIPreparedModel>>& preparedModels, |
| const hal::hidl_vec<hal::BufferRole>& inputRoles, |
| const hal::hidl_vec<hal::BufferRole>& outputRoles) const; |
| |
| /** |
| * Blocks until the device is not in a bad state. |
| * |
| * @return Error code after waiting. ANEURALNETWORKS_NO_ERROR if device is |
| * not in a bad state. |
| */ |
| int wait() const; |
| |
| private: |
| // Cached initialization results. |
| const hal::Capabilities kCapabilities; |
| const std::vector<hal::Extension> kSupportedExtensions; |
| const int32_t kType; |
| const std::string kVersionString; |
| const std::pair<uint32_t, uint32_t> kNumberOfCacheFilesNeeded; |
| |
| // internal methods to prepare a model |
| std::pair<int, std::shared_ptr<VersionedIPreparedModel>> prepareModelInternal( |
| const hal::Model& model, hal::ExecutionPreference preference, hal::Priority priority, |
| const std::optional<Deadline>& deadline, const std::string& cacheDir, |
| const std::optional<hal::CacheToken>& maybeToken) const; |
| std::pair<int, std::shared_ptr<VersionedIPreparedModel>> prepareModelFromCacheInternal( |
| const std::optional<Deadline>& deadline, const std::string& cacheDir, |
| const hal::CacheToken& token) const; |
| |
| /** |
| * This is a utility class for VersionedIDevice that encapsulates a |
| * V1_0::IDevice, any appropriate downcasts to newer interfaces, and a |
| * hidl_death_recipient that will proactively handle the case when the |
| * service containing the IDevice object crashes. |
| * |
| * This is a convenience class to help VersionedIDevice recover from an |
| * IDevice object crash: It bundles together all the data that needs to |
| * change when recovering from a crash, and simplifies the process of |
| * instantiating that data (at VersionedIDevice creation time) and |
| * re-instantiating that data (at crash recovery time). |
| */ |
| class Core { |
| public: |
| /** |
| * Constructor for the Core object. |
| * |
| * Core is constructed with a 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, allowing |
| * VersionedIDevice to default to using the latest version of all |
| * IDevice interface methods automatically. |
| * |
| * @param device A device object that is at least version 1.0 of the IDevice |
| * interface. |
| * @param deathHandler A hidl_death_recipient that will proactively handle |
| * the case when the service containing the IDevice |
| * object crashes. |
| */ |
| Core(sp<hal::V1_0::IDevice> device, sp<IDeviceDeathHandler> deathHandler); |
| |
| /** |
| * Destructor for the Core object. |
| * |
| * This destructor unlinksToDeath this object's hidl_death_recipient as it |
| * no longer needs to handle the case where the IDevice's service crashes. |
| */ |
| ~Core(); |
| |
| // Support move but not copy |
| Core(Core&&) noexcept; |
| Core& operator=(Core&&) noexcept; |
| Core(const Core&) = delete; |
| Core& operator=(const Core&) = delete; |
| |
| /** |
| * Create a Core object. |
| * |
| * Prefer using this function over the constructor, as it adds more |
| * protections. |
| * |
| * This call linksToDeath a hidl_death_recipient that can |
| * proactively handle the case when the service containing the IDevice |
| * object crashes. |
| * |
| * @param device A device object that is at least version 1.0 of the IDevice |
| * interface. |
| * @return A valid Core object, otherwise nullopt. |
| */ |
| static std::optional<Core> create(sp<hal::V1_0::IDevice> device); |
| |
| /** |
| * Returns sp<*::IDevice> that is a downcast of the sp<V1_0::IDevice> |
| * passed to the constructor. This will be nullptr if that IDevice is |
| * not actually of the specified downcast type. |
| */ |
| template <typename T_IDevice> |
| sp<T_IDevice> getDevice() const; |
| template <> |
| sp<hal::V1_0::IDevice> getDevice() const { |
| return mDeviceV1_0; |
| } |
| template <> |
| sp<hal::V1_1::IDevice> getDevice() const { |
| return mDeviceV1_1; |
| } |
| template <> |
| sp<hal::V1_2::IDevice> getDevice() const { |
| return mDeviceV1_2; |
| } |
| template <> |
| sp<hal::V1_3::IDevice> getDevice() const { |
| return mDeviceV1_3; |
| } |
| |
| /** |
| * Returns sp<*::IDevice> (as per getDevice()) and the |
| * hidl_death_recipient that will proactively handle the case when the |
| * service containing the IDevice object crashes. |
| */ |
| template <typename T_IDevice> |
| std::pair<sp<T_IDevice>, sp<IDeviceDeathHandler>> getDeviceAndDeathHandler() const; |
| |
| private: |
| /** |
| * All versions of IDevice are necessary because the driver could be v1.0, |
| * v1.1, or a later version. All these 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<hal::V1_0::IDevice> mDeviceV1_0; |
| sp<hal::V1_1::IDevice> mDeviceV1_1; |
| sp<hal::V1_2::IDevice> mDeviceV1_2; |
| sp<hal::V1_3::IDevice> mDeviceV1_3; |
| |
| /** |
| * HIDL callback to be invoked if the service for mDeviceV1_0 crashes. |
| * |
| * nullptr if this Core instance is a move victim and hence has no |
| * callback to be unlinked. |
| */ |
| sp<IDeviceDeathHandler> mDeathHandler; |
| }; |
| |
| // This method retrieves the appropriate mCore.mDevice* field, under a read lock. |
| template <typename T_IDevice> |
| sp<T_IDevice> getDevice() const EXCLUDES(mMutex) { |
| std::shared_lock lock(mMutex); |
| return mCore.getDevice<T_IDevice>(); |
| } |
| |
| // This method retrieves the appropriate mCore.mDevice* fields, under a read lock. |
| template <typename T_IDevice> |
| auto getDeviceAndDeathHandler() const EXCLUDES(mMutex) { |
| std::shared_lock lock(mMutex); |
| return mCore.getDeviceAndDeathHandler<T_IDevice>(); |
| } |
| |
| // This method calls the function fn in a manner that supports recovering |
| // from a driver crash: If the driver implementation is dead because the |
| // driver crashed either before the call to fn or during the call to fn, we |
| // will attempt to obtain a new instance of the same driver and call fn |
| // again. |
| // |
| // If a callback is provided, this method protects it against driver death |
| // and waits for it (callback->wait()). |
| template <typename T_Return, typename T_IDevice, typename T_Callback = std::nullptr_t> |
| hal::Return<T_Return> recoverable( |
| const char* context, |
| const std::function<hal::Return<T_Return>(const sp<T_IDevice>&)>& fn, |
| const T_Callback& callback = nullptr) const EXCLUDES(mMutex); |
| |
| // The name of the service that implements the driver. |
| const std::string kServiceName; |
| |
| // Factory function object to generate an IDevice object. |
| const hal::DeviceFactory kMakeDevice; |
| |
| // Guards access to mCore. |
| mutable std::shared_mutex mMutex; |
| |
| // Data that can be rewritten during driver recovery. Guarded againt |
| // synchronous access by a mutex: Any number of concurrent read accesses is |
| // permitted, but a write access excludes all other accesses. |
| mutable Core mCore GUARDED_BY(mMutex); |
| }; |
| |
| /** This class wraps an IPreparedModel object of any version. */ |
| class VersionedIPreparedModel { |
| DISALLOW_IMPLICIT_CONSTRUCTORS(VersionedIPreparedModel); |
| |
| public: |
| /** |
| * Constructor for the VersionedIPreparedModel object. |
| * |
| * This constructor should not be used directly. Instead, |
| * VersionedIPreparedModel should be created via |
| * VersionedIDevice::prepareModel*. |
| * |
| * VersionedIPreparedModel is constructed with the V1_0::IPreparedModel object, which |
| * represents a device that is at least v1.0 of the interface. The constructor downcasts |
| * to the latest version of the IPreparedModel interface, and will default to using the |
| * latest version of all IPreparedModel interface methods automatically. |
| * |
| * @param preparedModel A prepared model object that is least version 1.0 of the |
| * IPreparedModel interface. |
| * @param deathHandler A hidl_death_recipient that will proactively handle |
| * the case when the service containing the IDevice |
| * object crashes. |
| */ |
| VersionedIPreparedModel(sp<hal::V1_0::IPreparedModel> preparedModel, |
| sp<IPreparedModelDeathHandler> deathHandler); |
| |
| /** |
| * Destructor for the VersionedIPreparedModel object. |
| * |
| * This destructor unlinksToDeath this object's hidl_death_recipient as it |
| * no longer needs to handle the case where the IPreparedModel's service |
| * crashes. |
| */ |
| ~VersionedIPreparedModel(); |
| |
| /** |
| * Performs a synchronous execution on a prepared model. |
| * |
| * The execution is performed synchronously with respect to the caller. |
| * VersionedIPreparedModel::execute must verify the inputs to the function |
| * are correct. If there is an error, VersionedIPreparedModel::execute must |
| * immediately return with the appropriate result code. If the inputs to the |
| * function are valid and there is no error, |
| * VersionedIPreparedModel::execute must perform the execution, and must not |
| * return until the execution is complete. |
| * |
| * If the prepared model was prepared from a model wherein all tensor |
| * operands have fully specified dimensions, and the inputs to the function |
| * are valid, and at execution time every operation's input operands have |
| * legal values, then the execution should complete successfully |
| * (ANEURALNETWORKS_NO_ERROR): There must be no failure unless the device |
| * itself is in a bad state. |
| * |
| * execute may be called with an optional deadline. If the execution is not |
| * able to be completed before the provided deadline, the execution may be |
| * aborted, and either {@link ErrorStatus::MISSED_DEADLINE_TRANSIENT} or |
| * {@link ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The |
| * error due to an abort must be sent the same way as other errors, |
| * described above. |
| * |
| * Any number of calls to the VersionedIPreparedModel::execute function, in |
| * any combination, may be made concurrently, even on the same |
| * VersionedIPreparedModel object. |
| * |
| * @param request The input and output information on which the prepared |
| * model is to be executed. |
| * @param measure Specifies whether or not to measure duration of the |
| * execution. |
| * @param deadline Optional time point. If provided, prepareModel is |
| * expected to complete by this time point. If it is not able to be |
| * completed by the deadline, the execution may be aborted. |
| * @param loopTimeoutDuration The maximum amount of time that should be spent |
| * executing a {@link OperationType::WHILE} operation. If a loop |
| * condition model does not output false within this duration, the |
| * execution must be aborted. If no loop timeout duration is provided, |
| * the maximum amount of time is {@link LoopTimeoutDurationNs::DEFAULT}. |
| * When provided, the duration must not exceed {@link |
| * LoopTimeoutDurationNs::MAXIMUM}. |
| * @param preferSynchronous 'true' to perform synchronous HAL execution when |
| * possible, 'false' to force asynchronous HAL execution. |
| * @return A tuple consisting of: |
| * - Result code of the execution, must be: |
| * - ANEURALNETWORKS_NO_ERROR if execution is performed successfully |
| * - ANEURALNETWORKS_UNAVAILABLE_DEVICE if driver is offline or busy |
| * - ANEURALNETWORKS_OP_FAILED if there is an unspecified error |
| * - ANEURALNETWORKS_OUTPUT_INSUFFICIENT_SIZE if at least one output |
| * operand buffer is not large enough to store the corresponding |
| * output |
| * - ANEURALNETWORKS_BAD_DATA if one of the input arguments is |
| * invalid |
| * - 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 result code is either |
| * ANEURALNETWORKS_NO_ERROR or |
| * ANEURALNETWORKS_OUTPUT_INSUFFICIENT_SIZE. outputShapes may be |
| * empty if the result code is ANEURALNETWORKS_NO_ERROR and all |
| * model output operands are fully-specified at execution time. |
| * outputShapes must have the same number of elements as the number |
| * of model output operands if the result code is |
| * ANEURALNETWORKS_OUTPUT_INSUFFICIENT_SIZE, or if the result code |
| * is ANEURALNETWORKS_NO_ERROR and the model has at least one output |
| * operand that is not fully-specified. |
| * - Duration of execution. Unless measure is YES and result code is |
| * ANEURALNETWORKS_NO_ERROR, all times must be reported as |
| * UINT64_MAX. A driver may choose to report any time as UINT64_MAX, |
| * indicating that measurement is not available. |
| */ |
| std::tuple<int, std::vector<hal::OutputShape>, hal::Timing> execute( |
| const hal::Request& request, hal::MeasureTiming measure, |
| const std::optional<Deadline>& deadline, |
| const hal::OptionalTimeoutDuration& loopTimeoutDuration, bool preferSynchronous) const; |
| |
| /** |
| * Creates a burst controller on a prepared model. |
| * |
| * @param preferPowerOverLatency 'true' if the Burst object should run in a |
| * more power efficient mode, 'false' if more |
| * power can be used to possibly reduce |
| * burst compute latency. |
| * @return ExecutionBurstController Execution burst controller object. |
| * nullptr is returned if the burst cannot |
| * be configured for any reason. |
| */ |
| std::shared_ptr<ExecutionBurstController> configureExecutionBurst( |
| bool preferPowerOverLatency) const; |
| |
| /** |
| * 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. If there is an error during validation, |
| * executeFenced must immediately return with the corresponding ErrorStatus. If the inputs |
| * to the function are valid and there is no error 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 syncFence. If the |
| * asynchronous task fails to launch, executeFenced must immediately return with |
| * ErrorStatus::GENERAL_FAILURE, an empty handle for the syncFence, and nullptr |
| * for callback. The execution must wait for all the sync fences (if any) in waitFor to be |
| * signaled before starting the actual execution. |
| * |
| * If any of sync fences in waitFor changes to error status after the executeFenced |
| * call succeeds, the driver must immediately set the returned syncFence to error status. |
| * |
| * When the asynchronous task has finished its execution, it must |
| * immediately signal the syncFence returned from executeFenced call. After |
| * the syncFence 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}). |
| * |
| * executeFenced may be called with an optional deadline and an optional |
| * timeoutDurationAfterFence. If the execution is not able to be completed |
| * before the provided deadline or within the timeoutDurationAfterFence, |
| * whichever comes earlier, the execution may be aborted, and either {@link |
| * ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link |
| * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due |
| * to an abort must be sent the same way as other errors, described above. |
| * |
| * 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. The execution must |
| * wait for all sync fence to be signaled before starting the |
| * task. |
| * @param measure Specifies whether or not to measure duration of the execution. |
| * @param deadline The time by which execution is expected to complete. If |
| * the execution cannot be finished by the deadline, the |
| * execution may be aborted. |
| * @param loopTimeoutDuration The maximum amount of time that should be spent |
| * executing a {@link OperationType::WHILE} operation. If a loop |
| * condition model does not output false within this duration, the |
| * execution must be aborted. If no loop timeout duration is provided, |
| * the maximum amount of time is {@link LoopTimeoutDurationNs::DEFAULT}. |
| * When provided, the duration must not exceed {@link |
| * LoopTimeoutDurationNs::MAXIMUM}. |
| * @param timeoutDurationAfterFence The timeout duration within which the |
| * execution is expected to complete after |
| * all sync fences in waitFor are signaled. |
| * @return A tuple consisting of: |
| * - Error code of the dispatch call. |
| * - A sync_fence that will be triggered when the task is completed. |
| * The sync_fence will be set to error if critical error occurs when doing |
| * actual evaluation. |
| * - A callback can be used to query information like duration |
| * and detailed runtime error status when the task is completed. |
| * - Optional timing information. Only useful if the call is simulated using |
| * sync execution. Either IFencedExecutionCallback will be |
| * returned or optional timing information is returned |
| */ |
| std::tuple<int, hal::hidl_handle, sp<hal::IFencedExecutionCallback>, hal::Timing> executeFenced( |
| const hal::Request& request, const hal::hidl_vec<hal::hidl_handle>& waitFor, |
| hal::MeasureTiming measure, const std::optional<Deadline>& deadline, |
| const hal::OptionalTimeoutDuration& loopTimeoutDuration, |
| const hal::OptionalTimeoutDuration& timeoutDurationAfterFence); |
| |
| private: |
| friend class VersionedIDevice; |
| |
| std::tuple<int, std::vector<hal::OutputShape>, hal::Timing> executeAsynchronously( |
| const hal::Request& request, hal::MeasureTiming timing, |
| const std::optional<Deadline>& deadline, |
| const hal::OptionalTimeoutDuration& loopTimeoutDuration) const; |
| std::tuple<int, std::vector<hal::OutputShape>, hal::Timing> executeSynchronously( |
| const hal::Request& request, hal::MeasureTiming measure, |
| const std::optional<Deadline>& deadline, |
| const hal::OptionalTimeoutDuration& loopTimeoutDuration) const; |
| |
| /** |
| * Returns sp<V1_3::IPreparedModel> that is a downcast of the sp<V1_0::IPreparedModel> |
| * passed to the constructor. This will be nullptr if that IPreparedModel is |
| * not actually of the specified downcast type. |
| */ |
| sp<hal::V1_3::IPreparedModel> getV1_3() const { return mPreparedModelV1_3; } |
| |
| /** |
| * All versions of IPreparedModel are necessary because the preparedModel could be v1.0, |
| * v1.2, or a later version. All these pointers logically represent the same object. |
| * |
| * The general strategy is: HIDL returns a V1_0 prepared model object, which |
| * (if not nullptr) could be v1.0, v1.2, or a greater version. The V1_0 |
| * object is then "dynamically cast" to objects of later versions. If successful, |
| * mPreparedModel* will point to the same object as mPreparedModelV1_0; otherwise, |
| * mPreparedModel* will be nullptr. |
| * |
| * In general: |
| * * If the prepared model is truly v1.0, mPreparedModelV1_0 will point to a valid object, |
| * both mPreparedModelV1_2 and mPreparedModelV1_3 will be nullptr. |
| * * If the prepared model is truly v1.2, both mPreparedModelV1_0 and mPreparedModelV1_2 |
| * will point to the same valid object, but mPreparedModelV1_3 will be nullptr. |
| * * If the prepared model is truly v1.3 or later, all of mPreparedModelV1_0, |
| * mPreparedModelV1_2, and mPreparedModelV1_3 will point to the same valid object. |
| * |
| * Idiomatic usage: if mPreparedModelV1_3 is non-null, do V1_3 dispatch; |
| * otherwise, if mPreparedModelV1_2 is non-null, do V1_2 dispatch; |
| * otherwise, do V1_0 dispatch. |
| */ |
| sp<hal::V1_0::IPreparedModel> mPreparedModelV1_0; |
| sp<hal::V1_2::IPreparedModel> mPreparedModelV1_2; |
| sp<hal::V1_3::IPreparedModel> mPreparedModelV1_3; |
| |
| /** |
| * HIDL callback to be invoked if the service for mPreparedModelV1_0 crashes. |
| */ |
| const sp<IPreparedModelDeathHandler> mDeathHandler; |
| }; |
| |
| } // namespace nn |
| } // namespace android |
| |
| #endif // ANDROID_FRAMEWORKS_ML_NN_RUNTIME_VERSIONED_INTERFACES_H |