blob: 75cf0a523480f40bd3574dfecb92c46aa417e93e [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_FRAMEWORKS_ML_NN_COMMON_NNAPI_IDEVICE_H
#define ANDROID_FRAMEWORKS_ML_NN_COMMON_NNAPI_IDEVICE_H
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "nnapi/Result.h"
#include "nnapi/Types.h"
namespace android::nn {
// Forward declarations
class IBuffer;
class IPreparedModel;
/**
* This interface represents a device driver.
*
* This interface is thread-safe, and any class that implements this interface must be thread-safe.
*/
class IDevice {
public:
/**
* Returns the name of the driver.
*
* @return Name of the driver.
*/
virtual const std::string& getName() const = 0;
/**
* 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
* disallow 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. Must have nonzero length.
*/
virtual const std::string& getVersionString() const = 0;
/**
* Returns the feature level of a driver.
*
* @return featureLevel The API level of the most advanced feature this driver implements.
*/
virtual Version getFeatureLevel() const = 0;
/**
* Returns the device type of a driver.
*
* The device type can be used to 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.
*
* @return type The DeviceType of the device. Please note, this is not a bitfield of
* DeviceTypes. Each device must only be of a single DeviceType.
*/
virtual DeviceType getType() const = 0;
/**
* 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.
*/
virtual const std::vector<Extension>& getSupportedExtensions() const = 0;
/**
* Gets the capabilities of a driver.
*
* @return capabilities Capabilities of the driver.
*/
virtual const Capabilities& getCapabilities() const = 0;
/**
* 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.
*
* IDevice::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.
*
* @return A pair of:
* - 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
* ::android::nn::kMaxNumberOfCacheFiles.
* - 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
* ::android::nn::kMaxNumberOfCacheFiles.
*/
virtual std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const = 0;
/**
* Blocks until the device is not in a bad state.
*
* @return Nothing on success, otherwise GeneralError.
*/
virtual GeneralResult<void> wait() const = 0;
/**
* Gets the supported operations in a model.
*
* IDevice::getSupportedOperations indicates which operations of the top-level subgraph are
* fully supported by the vendor driver. If an operation may not be supported for any reason,
* IDevice::getSupportedOperations must return `false` for that operation.
*
* The {@link OperationType::IF} and {@link OperationType::WHILE} operations may only be fully
* supported if the vendor driver fully supports all operations in the referenced subgraphs.
*
* @param model A Model whose operations--and their corresponding operands--are to be verified
* by the driver.
* @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.
*/
virtual GeneralResult<std::vector<bool>> getSupportedOperations(const Model& model) const = 0;
/**
* Creates a prepared model for execution.
*
* IDevice::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 save the prepared model to
* cache files for faster model compilation time when the same model preparation is requested in
* the future. There are two types of cache file handles provided to the driver: model cache and
* data cache. For more information on the two types of cache handles, refer to
* IDevice::getNumberOfCacheFilesNeeded.
*
* The file descriptors must be opened with read and write permission. A file may have any size,
* and the corresponding file descriptor may have any offset. The driver must truncate a file to
* zero size before writing to that file. The file descriptors may be closed by the client once
* the preparation has finished. The driver must dup a file descriptor if it wants to get access
* to the cache file later.
*
* IDevice::prepareModel must verify its inputs related to preparing the model (as opposed to
* saving the prepared model to cache) are correct. If there is an error, IDevice::prepareModel
* must immediately return {@link ErrorStatus::INVALID_ARGUMENT} as a GeneralError. If the
* inputs to IDevice::prepareModel are valid and there is no error, IDevice::prepareModel must
* prepare the model.
*
* The model is prepared with a priority. This priority is relative to other prepared models
* owned by the same client. Higher priority executions may use more compute resources than
* lower priority executions, and may preempt or starve lower priority executions.
*
* IDevice::prepareModel can be called with an optional deadline. If the model is not able to be
* prepared before the provided deadline, the model preparation may be aborted, and either
* {@link ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
* ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned as a GeneralError.
*
* Optionally, the driver may save the prepared model to cache during the preparation. Any error
* that occurs when saving to cache must not affect the status of preparing the model. Even if
* the input arguments related to the cache may be invalid, or the driver may fail to save to
* cache, IDevice::prepareModel must finish preparing the model. The driver may choose not to
* save to cache even if the caching information is provided and valid.
*
* 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.
*
* @param model 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 modelCache A vector of handles with each entry holding exactly one cache file
* descriptor for the security-sensitive cache. The length of the vector must either be 0
* indicating that caching information is not provided, or match numModelCache returned from
* IDevice::getNumberOfCacheFilesNeeded. The cache handles will be provided in the same
* order when retrieving the preparedModel from cache files with
* IDevice::prepareModelFromCache.
* @param dataCache A vector of handles with each entry holding exactly one cache file
* descriptor for the constants' cache. The length of the vector must either be 0 indicating
* that caching information is not provided, or match numDataCache returned from
* IDevice::getNumberOfCacheFilesNeeded. The cache handles will be provided in the same
* order when retrieving the preparedModel from cache files with
* IDevice::prepareModelFromCache.
* @param token An caching token of length ::android::nn::kByteSizeOfCacheToken identifying the
* prepared model. The same token will be provided when retrieving the prepared model from
* the cache files with IDevice::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 preparedModel An IPreparedModel object representing a model that has been prepared
* for execution, otherwise GeneralError.
*/
virtual GeneralResult<SharedPreparedModel> prepareModel(
const Model& model, ExecutionPreference preference, Priority priority,
OptionalTimePoint deadline, const std::vector<SharedHandle>& modelCache,
const std::vector<SharedHandle>& dataCache, const CacheToken& token) const = 0;
/**
* Creates a prepared model from cache files for execution.
*
* IDevice::prepareModelFromCache is used to retrieve a prepared model directly from cache files
* to avoid slow model compilation time. There are two types of cache file handles provided to
* the driver: model cache and data cache. For more information on the two types of cache
* handles, refer to IDevice::getNumberOfCacheFilesNeeded.
*
* The file descriptors must be opened with read and write permission. A file may have any size,
* and the corresponding file descriptor may have any offset. The driver must truncate a file to
* zero size before writing to that file. The file descriptors may be closed by the client once
* the preparation has finished. The driver must dup a file descriptor if it wants to get access
* to the cache file later.
*
* IDevice::prepareModelFromCache must verify its inputs are correct, and that the
* security-sensitive cache has not been modified since it was last written by the driver. If
* there is an error, or if compilation caching is not supported, or if the security-sensitive
* cache has been modified, IDevice::prepareModelFromCache must immediately return {@link
* ErrorStatus::INVALID_ARGUMENT} as a GeneralError. If the inputs to
* IDevice::prepareModelFromCache are valid, the security-sensitive cache is not modified, and
* there is no error, IDevice::prepareModelFromCache must prepare the model
*
* IDevice::prepareModelFromCache can be called with an optional deadline. If the model is not
* able to prepared before the provided deadline, the model preparation may be aborted, and
* either {@link ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
* ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned as a GeneralError.
*
* 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.
*
* @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 modelCache A vector of handles with each entry holding exactly one cache file
* descriptor for the security-sensitive cache. The length of the vector must match the
* numModelCache returned from IDevice::getNumberOfCacheFilesNeeded. The cache handles will
* be provided in the same order as with IDevice::prepareModel.
* @param dataCache A vector of handles with each entry holding exactly one cache file
* descriptor for the constants' cache. The length of the vector must match the numDataCache
* returned from IDevice::getNumberOfCacheFilesNeeded. The cache handles will be provided in
* the same order as with IDevice::prepareModel.
* @param token A caching token of length ::android::nn::kByteSizeOfCacheToken identifying the
* prepared model. It is the same token provided when saving the cache files with
* IDevice::prepareModel. 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.
* @return preparedModel An IPreparedModel object representing a model that has been prepared
* for execution, otherwise GeneralError.
*/
virtual GeneralResult<SharedPreparedModel> prepareModelFromCache(
OptionalTimePoint deadline, const std::vector<SharedHandle>& modelCache,
const std::vector<SharedHandle>& dataCache, const CacheToken& token) const = 0;
/**
* Allocates a driver-managed buffer with the properties specified by the descriptor as well as
* the input and output roles of prepared models.
*
* IDevice::allocate must verify its inputs are correct. If there is an error, or if a certain
* role or property is not supported by the driver, IDevice::allocate must return with {@link
* ErrorStatus::INVALID_ARGUMENT} as a GeneralError. If the allocation is successful, this
* method must return the produced IBuffer. 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 The allocated IBuffer object. If the buffer was unable to be allocated due to an
* error, a GeneralError is returned instead.
*/
virtual GeneralResult<SharedBuffer> allocate(
const BufferDesc& desc, const std::vector<SharedPreparedModel>& preparedModels,
const std::vector<BufferRole>& inputRoles,
const std::vector<BufferRole>& outputRoles) const = 0;
// Public virtual destructor to allow objects to be stored (and destroyed) as smart pointers.
// E.g., std::unique_ptr<IDevice>.
virtual ~IDevice() = default;
protected:
// Protect the non-destructor special member functions to prevent object slicing.
IDevice() = default;
IDevice(const IDevice&) = default;
IDevice(IDevice&&) noexcept = default;
IDevice& operator=(const IDevice&) = default;
IDevice& operator=(IDevice&&) noexcept = default;
};
} // namespace android::nn
#endif // ANDROID_FRAMEWORKS_ML_NN_COMMON_NNAPI_IDEVICE_H