226 lines
8.6 KiB
C++
226 lines
8.6 KiB
C++
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
|
|
|
|
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.
|
|
==============================================================================*/
|
|
#include "tensorflow/lite/nnapi/nnapi_implementation.h"
|
|
|
|
#include <dlfcn.h>
|
|
#include <fcntl.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include <cstdlib>
|
|
|
|
#ifdef __ANDROID__
|
|
#include <sys/system_properties.h>
|
|
#endif // __ANDROID__
|
|
|
|
#define NNAPI_LOG(format, ...) fprintf(stderr, format "\n", __VA_ARGS__);
|
|
|
|
namespace {
|
|
|
|
#ifdef __ANDROID__
|
|
int32_t GetAndroidSdkVersion() {
|
|
const char* sdkProp = "ro.build.version.sdk";
|
|
char sdkVersion[PROP_VALUE_MAX];
|
|
int length = __system_property_get(sdkProp, sdkVersion);
|
|
if (length != 0) {
|
|
int32_t result = 0;
|
|
for (int i = 0; i < length; ++i) {
|
|
int digit = sdkVersion[i] - '0';
|
|
if (digit < 0 || digit > 9) {
|
|
// Non-numeric SDK version, assume it's higher than expected;
|
|
return 0xffff;
|
|
}
|
|
result = result * 10 + digit;
|
|
}
|
|
// TODO(levp): remove once SDK gets updated to 29th level
|
|
// Upgrade SDK version for pre-release Q to be able to test functionality
|
|
// available from SDK level 29.
|
|
if (result == 28) {
|
|
char versionCodename[PROP_VALUE_MAX];
|
|
const char* versionCodenameProp = "ro.build.version.codename";
|
|
length = __system_property_get(versionCodenameProp, versionCodename);
|
|
if (length != 0) {
|
|
if (versionCodename[0] == 'Q') {
|
|
return 29;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif // __ANDROID__
|
|
|
|
void* LoadFunction(void* handle, const char* name, bool optional) {
|
|
if (handle == nullptr) {
|
|
return nullptr;
|
|
}
|
|
void* fn = dlsym(handle, name);
|
|
if (fn == nullptr && !optional) {
|
|
NNAPI_LOG("nnapi error: unable to open function %s", name);
|
|
}
|
|
return fn;
|
|
}
|
|
|
|
#ifndef __ANDROID__
|
|
// Add /dev/shm implementation of shared memory for non-Android platforms
|
|
int ASharedMemory_create(const char* name, size_t size) {
|
|
int fd = shm_open(name, O_RDWR | O_CREAT, 0644);
|
|
if (fd < 0) {
|
|
return fd;
|
|
}
|
|
int result = ftruncate(fd, size);
|
|
if (result < 0) {
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
return fd;
|
|
}
|
|
#endif // __ANDROID__
|
|
|
|
#define LOAD_FUNCTION(handle, name) \
|
|
nnapi.name = reinterpret_cast<name##_fn>( \
|
|
LoadFunction(handle, #name, /*optional*/ false));
|
|
|
|
#define LOAD_FUNCTION_OPTIONAL(handle, name) \
|
|
nnapi.name = reinterpret_cast<name##_fn>( \
|
|
LoadFunction(handle, #name, /*optional*/ true));
|
|
|
|
#define LOAD_FUNCTION_RENAME(handle, name, symbol) \
|
|
nnapi.name = reinterpret_cast<name##_fn>( \
|
|
LoadFunction(handle, symbol, /*optional*/ false));
|
|
|
|
const NnApi LoadNnApi() {
|
|
NnApi nnapi = {};
|
|
nnapi.android_sdk_version = 0;
|
|
|
|
#ifdef __ANDROID__
|
|
nnapi.android_sdk_version = GetAndroidSdkVersion();
|
|
if (nnapi.android_sdk_version < 27) {
|
|
NNAPI_LOG("nnapi error: requires android sdk version to be at least %d",
|
|
27);
|
|
nnapi.nnapi_exists = false;
|
|
return nnapi;
|
|
}
|
|
#endif // __ANDROID__
|
|
|
|
void* libneuralnetworks = nullptr;
|
|
// TODO(b/123243014): change RTLD_LOCAL? Assumes there can be multiple
|
|
// instances of nn api RT
|
|
libneuralnetworks = dlopen("libneuralnetworks.so", RTLD_LAZY | RTLD_LOCAL);
|
|
if (libneuralnetworks == nullptr) {
|
|
NNAPI_LOG("nnapi error: unable to open library %s", "libneuralnetworks.so");
|
|
}
|
|
|
|
nnapi.nnapi_exists = libneuralnetworks != nullptr;
|
|
|
|
// API 27 (NN 1.0) methods.
|
|
LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksMemory_createFromFd);
|
|
LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksMemory_free);
|
|
LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksModel_create);
|
|
LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksModel_free);
|
|
LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksModel_finish);
|
|
LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksModel_addOperand);
|
|
LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksModel_setOperandValue);
|
|
LOAD_FUNCTION_OPTIONAL(
|
|
libneuralnetworks,
|
|
ANeuralNetworksModel_setOperandSymmPerChannelQuantParams);
|
|
LOAD_FUNCTION(libneuralnetworks,
|
|
ANeuralNetworksModel_setOperandValueFromMemory);
|
|
LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksModel_addOperation);
|
|
LOAD_FUNCTION(libneuralnetworks,
|
|
ANeuralNetworksModel_identifyInputsAndOutputs);
|
|
LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksCompilation_create);
|
|
LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksCompilation_free);
|
|
LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksCompilation_setPreference);
|
|
LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksCompilation_finish);
|
|
LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksExecution_create);
|
|
LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksExecution_free);
|
|
LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksExecution_setInput);
|
|
LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksExecution_setInputFromMemory);
|
|
LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksExecution_setOutput);
|
|
LOAD_FUNCTION(libneuralnetworks,
|
|
ANeuralNetworksExecution_setOutputFromMemory);
|
|
LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksExecution_startCompute);
|
|
LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksEvent_wait);
|
|
LOAD_FUNCTION(libneuralnetworks, ANeuralNetworksEvent_free);
|
|
|
|
// ASharedMemory_create has different implementations in Android depending on
|
|
// the partition. Generally it can be loaded from libandroid.so but in vendor
|
|
// partition (e.g. if a HAL wants to use NNAPI) it is only accessible through
|
|
// libcutils.
|
|
#ifdef __ANDROID__
|
|
void* libandroid = nullptr;
|
|
libandroid = dlopen("libandroid.so", RTLD_LAZY | RTLD_LOCAL);
|
|
if (libandroid != nullptr) {
|
|
LOAD_FUNCTION(libandroid, ASharedMemory_create);
|
|
} else {
|
|
void* cutils_handle = dlopen("libcutils.so", RTLD_LAZY | RTLD_LOCAL);
|
|
if (cutils_handle != nullptr) {
|
|
LOAD_FUNCTION_RENAME(cutils_handle, ASharedMemory_create,
|
|
"ashmem_create_region");
|
|
} else {
|
|
NNAPI_LOG("nnapi error: unable to open neither libraries %s and %s",
|
|
"libandroid.so", "libcutils.so");
|
|
}
|
|
}
|
|
#else
|
|
nnapi.ASharedMemory_create = ASharedMemory_create;
|
|
#endif // __ANDROID__
|
|
|
|
// API 28 (NN 1.1) methods.
|
|
LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
|
|
ANeuralNetworksModel_relaxComputationFloat32toFloat16);
|
|
|
|
// API 29 (NN 1.2) methods.
|
|
LOAD_FUNCTION_OPTIONAL(libneuralnetworks, ANeuralNetworks_getDeviceCount);
|
|
LOAD_FUNCTION_OPTIONAL(libneuralnetworks, ANeuralNetworks_getDevice);
|
|
LOAD_FUNCTION_OPTIONAL(libneuralnetworks, ANeuralNetworksDevice_getName);
|
|
LOAD_FUNCTION_OPTIONAL(libneuralnetworks, ANeuralNetworksDevice_getVersion);
|
|
LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
|
|
ANeuralNetworksDevice_getFeatureLevel);
|
|
LOAD_FUNCTION_OPTIONAL(libneuralnetworks, ANeuralNetworksDevice_getType);
|
|
LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
|
|
ANeuralNetworksModel_getSupportedOperationsForDevices);
|
|
LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
|
|
ANeuralNetworksCompilation_createForDevices);
|
|
LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
|
|
ANeuralNetworksCompilation_setCaching);
|
|
LOAD_FUNCTION_OPTIONAL(libneuralnetworks, ANeuralNetworksExecution_compute);
|
|
LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
|
|
ANeuralNetworksExecution_getOutputOperandRank);
|
|
LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
|
|
ANeuralNetworksExecution_getOutputOperandDimensions);
|
|
LOAD_FUNCTION_OPTIONAL(libneuralnetworks, ANeuralNetworksBurst_create);
|
|
LOAD_FUNCTION_OPTIONAL(libneuralnetworks, ANeuralNetworksBurst_free);
|
|
LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
|
|
ANeuralNetworksExecution_burstCompute);
|
|
LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
|
|
ANeuralNetworksMemory_createFromAHardwareBuffer);
|
|
LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
|
|
ANeuralNetworksExecution_setMeasureTiming);
|
|
LOAD_FUNCTION_OPTIONAL(libneuralnetworks,
|
|
ANeuralNetworksExecution_getDuration);
|
|
return nnapi;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
const NnApi* NnApiImplementation() {
|
|
static const NnApi nnapi = LoadNnApi();
|
|
return &nnapi;
|
|
}
|