| /* |
| * Copyright (C) 2017 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. |
| */ |
| |
| // Provides C++ classes to more easily use the Neural Networks API. |
| |
| #ifndef ANDROID_FRAMEWORKS_ML_NN_RUNTIME_NEURAL_NETWORKS_WRAPPER_H |
| #define ANDROID_FRAMEWORKS_ML_NN_RUNTIME_NEURAL_NETWORKS_WRAPPER_H |
| |
| #include <assert.h> |
| #include <math.h> |
| |
| #include <algorithm> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #ifdef NNTEST_SLTS |
| #include "SupportLibrary.h" |
| #else |
| #include "NeuralNetworks.h" |
| #endif |
| |
| namespace android { |
| namespace nn { |
| namespace wrapper { |
| |
| enum class Type { |
| FLOAT32 = ANEURALNETWORKS_FLOAT32, |
| INT32 = ANEURALNETWORKS_INT32, |
| UINT32 = ANEURALNETWORKS_UINT32, |
| TENSOR_FLOAT32 = ANEURALNETWORKS_TENSOR_FLOAT32, |
| TENSOR_INT32 = ANEURALNETWORKS_TENSOR_INT32, |
| TENSOR_QUANT8_ASYMM = ANEURALNETWORKS_TENSOR_QUANT8_ASYMM, |
| BOOL = ANEURALNETWORKS_BOOL, |
| TENSOR_QUANT16_SYMM = ANEURALNETWORKS_TENSOR_QUANT16_SYMM, |
| TENSOR_FLOAT16 = ANEURALNETWORKS_TENSOR_FLOAT16, |
| TENSOR_BOOL8 = ANEURALNETWORKS_TENSOR_BOOL8, |
| FLOAT16 = ANEURALNETWORKS_FLOAT16, |
| TENSOR_QUANT8_SYMM_PER_CHANNEL = ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL, |
| TENSOR_QUANT16_ASYMM = ANEURALNETWORKS_TENSOR_QUANT16_ASYMM, |
| TENSOR_QUANT8_SYMM = ANEURALNETWORKS_TENSOR_QUANT8_SYMM, |
| TENSOR_QUANT8_ASYMM_SIGNED = ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED, |
| MODEL = ANEURALNETWORKS_MODEL, |
| }; |
| |
| enum class ExecutePreference { |
| PREFER_LOW_POWER = ANEURALNETWORKS_PREFER_LOW_POWER, |
| PREFER_FAST_SINGLE_ANSWER = ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER, |
| PREFER_SUSTAINED_SPEED = ANEURALNETWORKS_PREFER_SUSTAINED_SPEED |
| }; |
| |
| enum class Duration { |
| ON_HARDWARE = ANEURALNETWORKS_DURATION_ON_HARDWARE, |
| IN_DRIVER = ANEURALNETWORKS_DURATION_IN_DRIVER, |
| FENCED_ON_HARDWARE = ANEURALNETWORKS_FENCED_DURATION_ON_HARDWARE, |
| FENCED_IN_DRIVER = ANEURALNETWORKS_FENCED_DURATION_IN_DRIVER, |
| }; |
| |
| enum class ExecutePriority { |
| LOW = ANEURALNETWORKS_PRIORITY_LOW, |
| MEDIUM = ANEURALNETWORKS_PRIORITY_MEDIUM, |
| HIGH = ANEURALNETWORKS_PRIORITY_HIGH, |
| DEFAULT = ANEURALNETWORKS_PRIORITY_DEFAULT, |
| }; |
| |
| enum class Result { |
| NO_ERROR = ANEURALNETWORKS_NO_ERROR, |
| OUT_OF_MEMORY = ANEURALNETWORKS_OUT_OF_MEMORY, |
| INCOMPLETE = ANEURALNETWORKS_INCOMPLETE, |
| UNEXPECTED_NULL = ANEURALNETWORKS_UNEXPECTED_NULL, |
| BAD_DATA = ANEURALNETWORKS_BAD_DATA, |
| OP_FAILED = ANEURALNETWORKS_OP_FAILED, |
| UNMAPPABLE = ANEURALNETWORKS_UNMAPPABLE, |
| BAD_STATE = ANEURALNETWORKS_BAD_STATE, |
| OUTPUT_INSUFFICIENT_SIZE = ANEURALNETWORKS_OUTPUT_INSUFFICIENT_SIZE, |
| UNAVAILABLE_DEVICE = ANEURALNETWORKS_UNAVAILABLE_DEVICE, |
| MISSED_DEADLINE_TRANSIENT = ANEURALNETWORKS_MISSED_DEADLINE_TRANSIENT, |
| MISSED_DEADLINE_PERSISTENT = ANEURALNETWORKS_MISSED_DEADLINE_PERSISTENT, |
| |
| // Functionality needed for this feature is not available on the current device. |
| FEATURE_LEVEL_TOO_LOW = 100001, |
| }; |
| |
| struct SymmPerChannelQuantParams { |
| ANeuralNetworksSymmPerChannelQuantParams params; |
| std::vector<float> scales; |
| |
| SymmPerChannelQuantParams(std::vector<float> scalesVec, uint32_t channelDim) |
| : scales(std::move(scalesVec)) { |
| params = { |
| .channelDim = channelDim, |
| .scaleCount = static_cast<uint32_t>(scales.size()), |
| .scales = scales.size() > 0 ? scales.data() : nullptr, |
| }; |
| } |
| |
| SymmPerChannelQuantParams(const SymmPerChannelQuantParams& other) |
| : params(other.params), scales(other.scales) { |
| params.scales = scales.size() > 0 ? scales.data() : nullptr; |
| } |
| |
| SymmPerChannelQuantParams& operator=(const SymmPerChannelQuantParams& other) { |
| if (this != &other) { |
| params = other.params; |
| scales = other.scales; |
| params.scales = scales.size() > 0 ? scales.data() : nullptr; |
| } |
| return *this; |
| } |
| }; |
| |
| struct OperandType { |
| ANeuralNetworksOperandType operandType; |
| std::vector<uint32_t> dimensions; |
| std::optional<SymmPerChannelQuantParams> channelQuant; |
| |
| OperandType(const OperandType& other) |
| : operandType(other.operandType), |
| dimensions(other.dimensions), |
| channelQuant(other.channelQuant) { |
| operandType.dimensions = dimensions.size() > 0 ? dimensions.data() : nullptr; |
| } |
| |
| OperandType& operator=(const OperandType& other) { |
| if (this != &other) { |
| operandType = other.operandType; |
| dimensions = other.dimensions; |
| channelQuant = other.channelQuant; |
| operandType.dimensions = dimensions.size() > 0 ? dimensions.data() : nullptr; |
| } |
| return *this; |
| } |
| |
| OperandType(Type type, std::vector<uint32_t> d, float scale = 0.0f, int32_t zeroPoint = 0) |
| : dimensions(std::move(d)), channelQuant(std::nullopt) { |
| operandType = { |
| .type = static_cast<int32_t>(type), |
| .dimensionCount = static_cast<uint32_t>(dimensions.size()), |
| .dimensions = dimensions.size() > 0 ? dimensions.data() : nullptr, |
| .scale = scale, |
| .zeroPoint = zeroPoint, |
| }; |
| } |
| |
| OperandType(Type type, std::vector<uint32_t> data, SymmPerChannelQuantParams&& channelQuant) |
| : dimensions(std::move(data)), channelQuant(std::move(channelQuant)) { |
| assert(type == Type::TENSOR_QUANT8_SYMM_PER_CHANNEL); |
| |
| operandType = { |
| .type = static_cast<int32_t>(type), |
| .dimensionCount = static_cast<uint32_t>(dimensions.size()), |
| .dimensions = dimensions.size() > 0 ? dimensions.data() : nullptr, |
| .scale = 0.0f, |
| .zeroPoint = 0, |
| }; |
| } |
| |
| void updateDimensions(std::vector<uint32_t> ndim) { |
| dimensions = ndim; |
| operandType.dimensions = dimensions.size() > 0 ? dimensions.data() : nullptr; |
| } |
| }; |
| |
| #ifdef NNTEST_SLTS |
| #define NNAPI_CALL(apiCall) mNnApi->apiCall |
| #else |
| #define NNAPI_CALL(apiCall) apiCall |
| #endif |
| |
| class Memory { |
| public: |
| #ifdef NNTEST_SLTS |
| // Takes ownership of a ANeuralNetworksMemory |
| Memory(const NnApiSupportLibrary* nnapi, ANeuralNetworksMemory* memory) |
| : mNnApi(nnapi), mMemory(memory) {} |
| |
| Memory(const NnApiSupportLibrary* nnapi, size_t size, int protect, int fd, size_t offset) |
| : mNnApi(nnapi) { |
| #else |
| Memory(size_t size, int protect, int fd, size_t offset) { |
| #endif |
| mValid = NNAPI_CALL(ANeuralNetworksMemory_createFromFd( |
| size, protect, fd, offset, &mMemory)) == ANEURALNETWORKS_NO_ERROR; |
| } |
| |
| #ifdef NNTEST_SLTS |
| Memory(const NnApiSupportLibrary* nnapi, AHardwareBuffer* buffer) : mNnApi(nnapi) { |
| #else |
| Memory(AHardwareBuffer* buffer) { |
| #endif |
| mValid = NNAPI_CALL(ANeuralNetworksMemory_createFromAHardwareBuffer(buffer, &mMemory)) == |
| ANEURALNETWORKS_NO_ERROR; |
| } |
| |
| ~Memory() { |
| if (mMemory) { |
| NNAPI_CALL(ANeuralNetworksMemory_free(mMemory)); |
| } |
| } |
| |
| // Disallow copy semantics to ensure the runtime object can only be freed |
| // once. Copy semantics could be enabled if some sort of reference counting |
| // or deep-copy system for runtime objects is added later. |
| Memory(const Memory&) = delete; |
| Memory& operator=(const Memory&) = delete; |
| |
| // Move semantics to remove access to the runtime object from the wrapper |
| // object that is being moved. This ensures the runtime object will be |
| // freed only once. |
| Memory(Memory&& other) { *this = std::move(other); } |
| Memory& operator=(Memory&& other) { |
| if (this != &other) { |
| if (mMemory) { |
| NNAPI_CALL(ANeuralNetworksMemory_free(mMemory)); |
| } |
| mMemory = other.mMemory; |
| mValid = other.mValid; |
| other.mMemory = nullptr; |
| other.mValid = false; |
| } |
| return *this; |
| } |
| |
| ANeuralNetworksMemory* get() const { return mMemory; } |
| bool isValid() const { return mValid; } |
| |
| private: |
| #ifdef NNTEST_SLTS |
| const NnApiSupportLibrary* mNnApi = nullptr; |
| #endif |
| ANeuralNetworksMemory* mMemory = nullptr; |
| bool mValid = true; |
| }; |
| |
| class Model { |
| public: |
| #ifdef NNTEST_SLTS |
| Model(const NnApiSupportLibrary* nnapi) : mNnApi(nnapi) { |
| #else |
| Model() { |
| #endif |
| // TODO handle the value returned by this call |
| NNAPI_CALL(ANeuralNetworksModel_create(&mModel)); |
| } |
| ~Model() { |
| if (mModel) { |
| NNAPI_CALL(ANeuralNetworksModel_free(mModel)); |
| } |
| } |
| |
| // Disallow copy semantics to ensure the runtime object can only be freed |
| // once. Copy semantics could be enabled if some sort of reference counting |
| // or deep-copy system for runtime objects is added later. |
| Model(const Model&) = delete; |
| Model& operator=(const Model&) = delete; |
| |
| // Move semantics to remove access to the runtime object from the wrapper |
| // object that is being moved. This ensures the runtime object will be |
| // freed only once. |
| Model(Model&& other) { *this = std::move(other); } |
| Model& operator=(Model&& other) { |
| if (this != &other) { |
| if (mModel) { |
| NNAPI_CALL(ANeuralNetworksModel_free(mModel)); |
| } |
| mModel = other.mModel; |
| mNextOperandId = other.mNextOperandId; |
| mValid = other.mValid; |
| other.mModel = nullptr; |
| other.mNextOperandId = 0; |
| other.mValid = false; |
| } |
| return *this; |
| } |
| |
| Result finish() { |
| if (mValid) { |
| auto result = static_cast<Result>(NNAPI_CALL(ANeuralNetworksModel_finish(mModel))); |
| if (result != Result::NO_ERROR) { |
| mValid = false; |
| } |
| return result; |
| } else { |
| return Result::BAD_STATE; |
| } |
| } |
| |
| uint32_t addOperand(const OperandType* type) { |
| if (NNAPI_CALL(ANeuralNetworksModel_addOperand(mModel, &(type->operandType))) != |
| ANEURALNETWORKS_NO_ERROR) { |
| mValid = false; |
| } |
| if (type->channelQuant) { |
| if (NNAPI_CALL(ANeuralNetworksModel_setOperandSymmPerChannelQuantParams( |
| mModel, mNextOperandId, &type->channelQuant.value().params)) != |
| ANEURALNETWORKS_NO_ERROR) { |
| mValid = false; |
| } |
| } |
| return mNextOperandId++; |
| } |
| |
| void setOperandValue(uint32_t index, const void* buffer, size_t length) { |
| if (NNAPI_CALL(ANeuralNetworksModel_setOperandValue(mModel, index, buffer, length)) != |
| ANEURALNETWORKS_NO_ERROR) { |
| mValid = false; |
| } |
| } |
| |
| void setOperandValueFromMemory(uint32_t index, const Memory* memory, uint32_t offset, |
| size_t length) { |
| if (NNAPI_CALL(ANeuralNetworksModel_setOperandValueFromMemory( |
| mModel, index, memory->get(), offset, length)) != ANEURALNETWORKS_NO_ERROR) { |
| mValid = false; |
| } |
| } |
| |
| void addOperation(ANeuralNetworksOperationType type, const std::vector<uint32_t>& inputs, |
| const std::vector<uint32_t>& outputs) { |
| if (NNAPI_CALL(ANeuralNetworksModel_addOperation( |
| mModel, type, static_cast<uint32_t>(inputs.size()), inputs.data(), |
| static_cast<uint32_t>(outputs.size()), outputs.data())) != |
| ANEURALNETWORKS_NO_ERROR) { |
| mValid = false; |
| } |
| } |
| void identifyInputsAndOutputs(const std::vector<uint32_t>& inputs, |
| const std::vector<uint32_t>& outputs) { |
| if (NNAPI_CALL(ANeuralNetworksModel_identifyInputsAndOutputs( |
| mModel, static_cast<uint32_t>(inputs.size()), inputs.data(), |
| static_cast<uint32_t>(outputs.size()), outputs.data())) != |
| ANEURALNETWORKS_NO_ERROR) { |
| mValid = false; |
| } |
| } |
| |
| void relaxComputationFloat32toFloat16(bool isRelax) { |
| if (NNAPI_CALL(ANeuralNetworksModel_relaxComputationFloat32toFloat16(mModel, isRelax)) == |
| ANEURALNETWORKS_NO_ERROR) { |
| mRelaxed = isRelax; |
| } |
| } |
| |
| ANeuralNetworksModel* getHandle() const { return mModel; } |
| bool isValid() const { return mValid; } |
| bool isRelaxed() const { return mRelaxed; } |
| |
| #ifdef NNTEST_SLTS |
| private: |
| const NnApiSupportLibrary* mNnApi = nullptr; |
| #endif |
| |
| protected: |
| ANeuralNetworksModel* mModel = nullptr; |
| // We keep track of the operand ID as a convenience to the caller. |
| uint32_t mNextOperandId = 0; |
| bool mValid = true; |
| bool mRelaxed = false; |
| }; |
| |
| class Event { |
| public: |
| #ifdef NNTEST_SLTS |
| Event(const NnApiSupportLibrary* nnapi) : mNnApi(nnapi) {} |
| Event(const NnApiSupportLibrary* nnapi, int syncFd) : mNnApi(nnapi) { |
| #else |
| Event() {} |
| Event(int syncFd) { |
| #endif |
| mValid = NNAPI_CALL(ANeuralNetworksEvent_createFromSyncFenceFd(syncFd, &mEvent)) == |
| ANEURALNETWORKS_NO_ERROR; |
| } |
| |
| ~Event() { |
| if (mEvent) { |
| NNAPI_CALL(ANeuralNetworksEvent_free(mEvent)); |
| } |
| } |
| |
| // Disallow copy semantics to ensure the runtime object can only be freed |
| // once. Copy semantics could be enabled if some sort of reference counting |
| // or deep-copy system for runtime objects is added later. |
| Event(const Event&) = delete; |
| Event& operator=(const Event&) = delete; |
| |
| // Move semantics to remove access to the runtime object from the wrapper |
| // object that is being moved. This ensures the runtime object will be |
| // freed only once. |
| Event(Event&& other) { *this = std::move(other); } |
| Event& operator=(Event&& other) { |
| if (this != &other) { |
| if (mEvent) { |
| NNAPI_CALL(ANeuralNetworksEvent_free(mEvent)); |
| } |
| #ifdef NNTEST_SLTS |
| mNnApi = other.mNnApi; |
| #endif |
| mEvent = other.mEvent; |
| other.mEvent = nullptr; |
| } |
| return *this; |
| } |
| |
| Result wait() { return static_cast<Result>(NNAPI_CALL(ANeuralNetworksEvent_wait(mEvent))); } |
| |
| // Only for use by Execution |
| void set(ANeuralNetworksEvent* newEvent) { |
| if (mEvent) { |
| NNAPI_CALL(ANeuralNetworksEvent_free(mEvent)); |
| } |
| mEvent = newEvent; |
| } |
| |
| // Only for use by Execution |
| ANeuralNetworksEvent* getHandle() const { return mEvent; } |
| |
| Result getSyncFenceFd(int* sync_fence_fd) { |
| return static_cast<Result>( |
| NNAPI_CALL(ANeuralNetworksEvent_getSyncFenceFd(mEvent, sync_fence_fd))); |
| } |
| |
| bool isValid() const { return mValid; } |
| |
| #ifdef NNTEST_SLTS |
| private: |
| const NnApiSupportLibrary* mNnApi = nullptr; |
| #endif |
| |
| private: |
| bool mValid = true; |
| ANeuralNetworksEvent* mEvent = nullptr; |
| }; |
| |
| class Compilation { |
| public: |
| #ifdef NNTEST_SLTS |
| // On success, createForDevice(s) will return Result::NO_ERROR and the created compilation; |
| // otherwise, it will return the error code and Compilation object wrapping a nullptr handle. |
| static std::pair<Result, Compilation> createForDevice(const NnApiSupportLibrary* nnapi, |
| const Model* model, |
| const ANeuralNetworksDevice* device) { |
| return createForDevices(nnapi, model, {device}); |
| } |
| static std::pair<Result, Compilation> createForDevices( |
| const NnApiSupportLibrary* nnapi, const Model* model, |
| const std::vector<const ANeuralNetworksDevice*>& devices) { |
| ANeuralNetworksCompilation* compilation = nullptr; |
| const Result result = |
| static_cast<Result>(nnapi->ANeuralNetworksCompilation_createForDevices( |
| model->getHandle(), devices.empty() ? nullptr : devices.data(), |
| devices.size(), &compilation)); |
| return {result, Compilation(nnapi, compilation)}; |
| } |
| #else |
| Compilation(const Model* model) { |
| int result = |
| NNAPI_CALL(ANeuralNetworksCompilation_create(model->getHandle(), &mCompilation)); |
| if (result != 0) { |
| // TODO Handle the error |
| } |
| } |
| #endif |
| |
| ~Compilation() { NNAPI_CALL(ANeuralNetworksCompilation_free(mCompilation)); } |
| |
| // Disallow copy semantics to ensure the runtime object can only be freed |
| // once. Copy semantics could be enabled if some sort of reference counting |
| // or deep-copy system for runtime objects is added later. |
| Compilation(const Compilation&) = delete; |
| Compilation& operator=(const Compilation&) = delete; |
| |
| // Move semantics to remove access to the runtime object from the wrapper |
| // object that is being moved. This ensures the runtime object will be |
| // freed only once. |
| Compilation(Compilation&& other) { *this = std::move(other); } |
| Compilation& operator=(Compilation&& other) { |
| if (this != &other) { |
| NNAPI_CALL(ANeuralNetworksCompilation_free(mCompilation)); |
| mCompilation = other.mCompilation; |
| other.mCompilation = nullptr; |
| } |
| return *this; |
| } |
| |
| Result setPreference(ExecutePreference preference) { |
| return static_cast<Result>(NNAPI_CALL(ANeuralNetworksCompilation_setPreference( |
| mCompilation, static_cast<int32_t>(preference)))); |
| } |
| |
| Result setPriority(ExecutePriority priority) { |
| return static_cast<Result>(NNAPI_CALL(ANeuralNetworksCompilation_setPriority( |
| mCompilation, static_cast<int32_t>(priority)))); |
| } |
| |
| Result setCaching(const std::string& cacheDir, const std::vector<uint8_t>& token) { |
| if (token.size() != ANEURALNETWORKS_BYTE_SIZE_OF_CACHE_TOKEN) { |
| return Result::BAD_DATA; |
| } |
| return static_cast<Result>(NNAPI_CALL(ANeuralNetworksCompilation_setCaching( |
| mCompilation, cacheDir.c_str(), token.data()))); |
| } |
| |
| Result finish() { |
| return static_cast<Result>(NNAPI_CALL(ANeuralNetworksCompilation_finish(mCompilation))); |
| } |
| |
| Result getPreferredMemoryAlignmentForInput(uint32_t index, uint32_t* alignment) const { |
| if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) { |
| return static_cast<Result>( |
| NNAPI_CALL(ANeuralNetworksCompilation_getPreferredMemoryAlignmentForInput( |
| mCompilation, index, alignment))); |
| } else { |
| return Result::FEATURE_LEVEL_TOO_LOW; |
| } |
| }; |
| |
| Result getPreferredMemoryPaddingForInput(uint32_t index, uint32_t* padding) const { |
| if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) { |
| return static_cast<Result>( |
| NNAPI_CALL(ANeuralNetworksCompilation_getPreferredMemoryPaddingForInput( |
| mCompilation, index, padding))); |
| } else { |
| return Result::FEATURE_LEVEL_TOO_LOW; |
| } |
| }; |
| |
| Result getPreferredMemoryAlignmentForOutput(uint32_t index, uint32_t* alignment) const { |
| if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) { |
| return static_cast<Result>( |
| NNAPI_CALL(ANeuralNetworksCompilation_getPreferredMemoryAlignmentForOutput( |
| mCompilation, index, alignment))); |
| } else { |
| return Result::FEATURE_LEVEL_TOO_LOW; |
| } |
| }; |
| |
| Result getPreferredMemoryPaddingForOutput(uint32_t index, uint32_t* padding) const { |
| if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) { |
| return static_cast<Result>( |
| NNAPI_CALL(ANeuralNetworksCompilation_getPreferredMemoryPaddingForOutput( |
| mCompilation, index, padding))); |
| } else { |
| return Result::FEATURE_LEVEL_TOO_LOW; |
| } |
| }; |
| |
| ANeuralNetworksCompilation* getHandle() const { return mCompilation; } |
| |
| #ifdef NNTEST_SLTS |
| protected: |
| // Takes the ownership of ANeuralNetworksCompilation. |
| Compilation(const NnApiSupportLibrary* nnapi, ANeuralNetworksCompilation* compilation) |
| : mNnApi(nnapi), mCompilation(compilation) {} |
| |
| private: |
| const NnApiSupportLibrary* mNnApi = nullptr; |
| #else |
| private: |
| #endif |
| ANeuralNetworksCompilation* mCompilation = nullptr; |
| }; |
| |
| class Execution { |
| public: |
| #ifdef NNTEST_SLTS |
| Execution(const NnApiSupportLibrary* nnapi, const Compilation* compilation) : mNnApi(nnapi) { |
| #else |
| Execution(const Compilation* compilation) { |
| #endif |
| int result = |
| NNAPI_CALL(ANeuralNetworksExecution_create(compilation->getHandle(), &mExecution)); |
| if (result != 0) { |
| // TODO Handle the error |
| } |
| } |
| |
| ~Execution() { |
| if (mExecution) { |
| NNAPI_CALL(ANeuralNetworksExecution_free(mExecution)); |
| } |
| } |
| |
| // Disallow copy semantics to ensure the runtime object can only be freed |
| // once. Copy semantics could be enabled if some sort of reference counting |
| // or deep-copy system for runtime objects is added later. |
| Execution(const Execution&) = delete; |
| Execution& operator=(const Execution&) = delete; |
| |
| // Move semantics to remove access to the runtime object from the wrapper |
| // object that is being moved. This ensures the runtime object will be |
| // freed only once. |
| Execution(Execution&& other) { *this = std::move(other); } |
| Execution& operator=(Execution&& other) { |
| if (this != &other) { |
| if (mExecution) { |
| NNAPI_CALL(ANeuralNetworksExecution_free(mExecution)); |
| } |
| mExecution = other.mExecution; |
| other.mExecution = nullptr; |
| } |
| return *this; |
| } |
| |
| Result setInput(uint32_t index, const void* buffer, size_t length, |
| const ANeuralNetworksOperandType* type = nullptr) { |
| return static_cast<Result>(NNAPI_CALL( |
| ANeuralNetworksExecution_setInput(mExecution, index, type, buffer, length))); |
| } |
| |
| Result setInputFromMemory(uint32_t index, const Memory* memory, uint32_t offset, |
| uint32_t length, const ANeuralNetworksOperandType* type = nullptr) { |
| return static_cast<Result>(NNAPI_CALL(ANeuralNetworksExecution_setInputFromMemory( |
| mExecution, index, type, memory->get(), offset, length))); |
| } |
| |
| Result setOutput(uint32_t index, void* buffer, size_t length, |
| const ANeuralNetworksOperandType* type = nullptr) { |
| return static_cast<Result>(NNAPI_CALL( |
| ANeuralNetworksExecution_setOutput(mExecution, index, type, buffer, length))); |
| } |
| |
| Result setOutputFromMemory(uint32_t index, const Memory* memory, uint32_t offset, |
| uint32_t length, const ANeuralNetworksOperandType* type = nullptr) { |
| return static_cast<Result>(NNAPI_CALL(ANeuralNetworksExecution_setOutputFromMemory( |
| mExecution, index, type, memory->get(), offset, length))); |
| } |
| |
| Result enableInputAndOutputPadding(bool enable) { |
| if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) { |
| return static_cast<Result>(NNAPI_CALL( |
| ANeuralNetworksExecution_enableInputAndOutputPadding(mExecution, enable))); |
| } else { |
| return Result::FEATURE_LEVEL_TOO_LOW; |
| } |
| } |
| |
| Result setReusable(bool reusable) { |
| if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) { |
| return static_cast<Result>( |
| NNAPI_CALL(ANeuralNetworksExecution_setReusable(mExecution, reusable))); |
| } else { |
| return Result::FEATURE_LEVEL_TOO_LOW; |
| } |
| } |
| |
| #ifndef NNTEST_SLTS |
| Result startCompute(Event* event) { |
| ANeuralNetworksEvent* ev = nullptr; |
| Result result = static_cast<Result>( |
| NNAPI_CALL(ANeuralNetworksExecution_startCompute(mExecution, &ev))); |
| event->set(ev); |
| return result; |
| } |
| |
| Result startComputeWithDependencies(const std::vector<const Event*>& dependencies, |
| uint64_t duration, Event* event) { |
| std::vector<const ANeuralNetworksEvent*> deps(dependencies.size()); |
| std::transform(dependencies.begin(), dependencies.end(), deps.begin(), |
| [](const Event* e) { return e->getHandle(); }); |
| ANeuralNetworksEvent* ev = nullptr; |
| Result result = static_cast<Result>( |
| NNAPI_CALL(ANeuralNetworksExecution_startComputeWithDependencies( |
| mExecution, deps.data(), deps.size(), duration, &ev))); |
| event->set(ev); |
| return result; |
| } |
| #endif |
| |
| Result compute() { |
| return static_cast<Result>(NNAPI_CALL(ANeuralNetworksExecution_compute(mExecution))); |
| } |
| |
| Result getOutputOperandDimensions(uint32_t index, std::vector<uint32_t>* dimensions) { |
| uint32_t rank = 0; |
| Result result = static_cast<Result>(NNAPI_CALL( |
| ANeuralNetworksExecution_getOutputOperandRank(mExecution, index, &rank))); |
| dimensions->resize(rank); |
| if ((result != Result::NO_ERROR && result != Result::OUTPUT_INSUFFICIENT_SIZE) || |
| rank == 0) { |
| return result; |
| } |
| result = static_cast<Result>(NNAPI_CALL(ANeuralNetworksExecution_getOutputOperandDimensions( |
| mExecution, index, dimensions->data()))); |
| return result; |
| } |
| |
| private: |
| #ifdef NNTEST_SLTS |
| const NnApiSupportLibrary* mNnApi = nullptr; |
| #endif |
| ANeuralNetworksExecution* mExecution = nullptr; |
| }; |
| |
| } // namespace wrapper |
| } // namespace nn |
| } // namespace android |
| |
| #endif // ANDROID_FRAMEWORKS_ML_NN_RUNTIME_NEURAL_NETWORKS_WRAPPER_H |