First implementation of the Neural Networks API. This first version can run a simple query on the CPU either via the fallback path or through a simulated driver. This code has many deficiencies: single threaded, not all validation are done, not going through HIDL, and not enough unit tests. Expect more changes! Test: Compiled and ran the unit tests Change-Id: I9f6a485a2e7207aeb5f91a2904dcb4b7fd8a6f65
diff --git a/runtime/NeuralNetworks.cpp b/runtime/NeuralNetworks.cpp new file mode 100644 index 0000000..73b2346 --- /dev/null +++ b/runtime/NeuralNetworks.cpp
@@ -0,0 +1,377 @@ +/* + * 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. + */ + +// Contains all the entry points to the C Neural Networks API. +// We do basic validation of the operands and then call the class +// that implements the functionality. + +#define LOG_TAG "NeuralNetworks" + +#include "NeuralNetworks.h" +#include "Manager.h" +#include "ModelBuilder.h" +#include "Request.h" + +#include <vector> + +using namespace android::nn; + +// Validates the type. The used dimensions can be underspecified. +static int ValidateOperandType(const ANeuralNetworksOperandType& type, const char* tag, + bool allowPartial) { + if (!allowPartial) { + for (uint32_t i = 0; i < type.dimensions.count; i++) { + if (type.dimensions.data[i] == 0) { + ALOGE("%s OperandType invalid dimensions[%u] = %u", tag, i, + type.dimensions.data[i]); + return ANEURALNETWORKS_BAD_DATA; + } + } + } + if (type.type >= ANEURALNETWORKS_NUMBER_DATA_TYPES) { + ALOGE("%s OperandType invalid type %u", tag, type.type); + return ANEURALNETWORKS_BAD_DATA; + } + /* TODO validate the quantization info. + if (type.offset != 0.f && type.scale == 0.f) { + ALOGE("%s OperandType invalid offset %f and scale %f", tag, type.offset, + type.scale); return ANEURALNETWORKS_BAD_DATA; + } + if (type.scale != 0.f && + (type.type == ANEURALNETWORKS_FLOAT16 || + type.type != ANEURALNETWORKS_FLOAT32)) { + ALOGE("%s OperandType scale %f with float type %u", tag, type.scale, + type.type); return ANEURALNETWORKS_BAD_DATA; + } + */ + return ANEURALNETWORKS_NO_ERROR; +} + +static int ValidateOperandList(const ANeuralNetworksIntList& list, uint32_t count, + const char* tag) { + for (uint32_t i = 0; i < list.count; i++) { + if (list.data[i] >= count) { + ALOGE("%s invalid operand index at %u = %u, count %u", tag, i, list.data[i], count); + return ANEURALNETWORKS_BAD_DATA; + } + } + return ANEURALNETWORKS_NO_ERROR; +} + +int ANeuralNetworksInitialize() { + DriverManager::get()->initialize(); + return ANEURALNETWORKS_NO_ERROR; +} + +void ANeuralNetworksShutdown() { + DriverManager::get()->shutdown(); +} + +int ANeuralNetworksModel_create(ANeuralNetworksModel** model) { + if (!model) { + ALOGE("ANeuralNetworksModel_create passed a nullptr"); + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + ModelBuilder* m = new ModelBuilder(); + if (m == nullptr) { + *model = nullptr; + return ANEURALNETWORKS_OUT_OF_MEMORY; + } + *model = reinterpret_cast<ANeuralNetworksModel*>(m); + return ANEURALNETWORKS_NO_ERROR; +} + +int ANeuralNetworksModel_createBaselineModel(ANeuralNetworksModel** model, uint32_t modelId) { + if (!model) { + ALOGE("ANeuralNetworksModel_create passed a nullptr"); + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + if (modelId >= ANEURALNETWORKS_NUMBER_BASELINE_MODELS) { + ALOGE("ANeuralNetworksModel_createBaselineModel invalid modelId %u", modelId); + return ANEURALNETWORKS_BAD_DATA; + } + + ModelBuilder* m = new ModelBuilder(); + if (m == nullptr) { + *model = nullptr; + return ANEURALNETWORKS_OUT_OF_MEMORY; + } + /* TODO uint32_t n = m->loadBaseLineModel(modelId); + if (n != ANEURALNETWORKS_NO_ERROR) { + delete m; + return n; + } + */ + *model = reinterpret_cast<ANeuralNetworksModel*>(m); + return ANEURALNETWORKS_NO_ERROR; +} + +void ANeuralNetworksModel_free(ANeuralNetworksModel* model) { + // No validation. Free of nullptr is valid. + ModelBuilder* m = reinterpret_cast<ModelBuilder*>(model); + delete m; +} + +int ANeuralNetworksModel_addOperand(ANeuralNetworksModel* model, + const ANeuralNetworksOperandType* type) { + if (!model || !type) { + ALOGE("ANeuralNetworksModel_addOperand passed a nullptr"); + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + ModelBuilder* m = reinterpret_cast<ModelBuilder*>(model); + int n = ValidateOperandType(*type, "ANeuralNetworksModel_addOperand", true); + if (n != ANEURALNETWORKS_NO_ERROR) { + return n; + } + return m->addOperand(*type); +} + +int ANeuralNetworksModel_setOperandValue(ANeuralNetworksModel* model, int32_t index, + const void* buffer, size_t length) { + if (!model || !buffer) { + ALOGE("ANeuralNetworksModel_setOperandValue passed a nullptr"); + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + ModelBuilder* m = reinterpret_cast<ModelBuilder*>(model); + return m->setOperandValue(index, buffer, length); +} + +int ANeuralNetworksModel_addOperation(ANeuralNetworksModel* model, + ANeuralNetworksOperationType type, + ANeuralNetworksIntList* inputs, + ANeuralNetworksIntList* outputs) { + if (!model || !inputs || !outputs) { + ALOGE("ANeuralNetworksModel_addOperation passed a nullptr"); + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + ModelBuilder* m = reinterpret_cast<ModelBuilder*>(model); + if (type >= ANEURALNETWORKS_NUMBER_OPERATION_TYPES) { + ALOGE("ANeuralNetworksModel_addOperation invalid operations type %u", type); + return ANEURALNETWORKS_BAD_DATA; + } + int n = ValidateOperandList(*inputs, m->operandCount(), + "ANeuralNetworksModel_addOperation inputs"); + if (n != ANEURALNETWORKS_NO_ERROR) { + return n; + } + n = ValidateOperandList(*outputs, m->operandCount(), + "ANeuralNetworksModel_addOperation outputs"); + if (n != ANEURALNETWORKS_NO_ERROR) { + return n; + } + + return m->addOperation(type, inputs, outputs); +} + +int ANeuralNetworksModel_setInputsAndOutputs(ANeuralNetworksModel* model, + ANeuralNetworksIntList* inputs, + ANeuralNetworksIntList* outputs) { + if (!model || !inputs || !outputs) { + ALOGE("ANeuralNetworksModel_setInputsAndOutputs passed a nullptr"); + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + ModelBuilder* m = reinterpret_cast<ModelBuilder*>(model); + int n = ValidateOperandList(*inputs, m->operandCount(), + "ANeuralNetworksModel_setInputsAndOutputs inputs"); + if (n != ANEURALNETWORKS_NO_ERROR) { + return n; + } + n = ValidateOperandList(*outputs, m->operandCount(), + "ANeuralNetworksModel_setInputsAndOutputs outputs"); + if (n != ANEURALNETWORKS_NO_ERROR) { + return n; + } + + return m->setInputsAndOutputs(inputs, outputs); +} + +int ANeuralNetworksModel_addSubModel(ANeuralNetworksModel* model, + const ANeuralNetworksModel* submodel, + ANeuralNetworksIntList* inputs, + ANeuralNetworksIntList* outputs) { + if (!model || !submodel) { + ALOGE("ANeuralNetworksModel_addSubModel passed a nullptr"); + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + ModelBuilder* m = reinterpret_cast<ModelBuilder*>(model); + int n = ValidateOperandList(*inputs, m->operandCount(), + "ANeuralNetworksModel_addSubModel inputs"); + if (n != ANEURALNETWORKS_NO_ERROR) { + return n; + } + n = ValidateOperandList(*outputs, m->operandCount(), + "ANeuralNetworksModel_addSubModel outputs"); + if (n != ANEURALNETWORKS_NO_ERROR) { + return n; + } + return ANEURALNETWORKS_NOT_IMPLEMENTED; +} + +int ANeuralNetworksModel_setBaselineId(ANeuralNetworksModel* model, uint32_t baseLineId) { + if (!model) { + ALOGE("ANeuralNetworksModel_setBaselineId passed a nullptr"); + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + if (baseLineId >= ANEURALNETWORKS_NUMBER_BASELINE_MODELS) { + ALOGE("ANeuralNetworksModel_setBaselineId invalid baselineId %u", baseLineId); + return ANEURALNETWORKS_BAD_DATA; + } + // TODO implement + return ANEURALNETWORKS_NOT_IMPLEMENTED; +} + +int ANeuralNetworksRequest_create(ANeuralNetworksModel* model, ANeuralNetworksRequest** request) { + if (!model || !request) { + ALOGE("ANeuralNetworksRequest_create passed a nullptr"); + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + + ModelBuilder* m = reinterpret_cast<ModelBuilder*>(model); + Request* r = m->createRequest(); + if (r == nullptr) { + *request = nullptr; + return ANEURALNETWORKS_OUT_OF_MEMORY; + } + *request = reinterpret_cast<ANeuralNetworksRequest*>(r); + return ANEURALNETWORKS_NO_ERROR; +} + +void ANeuralNetworksRequest_free(ANeuralNetworksRequest* request) { + // No validation. Free of nullptr is valid. + Request* r = reinterpret_cast<Request*>(request); + delete r; +} + +int ANeuralNetworksRequest_setPreference(ANeuralNetworksRequest* request, uint32_t preference) { + if (!request) { + ALOGE("ANeuralNetworksRequest_setPreference passed a nullptr"); + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + if (preference >= ANEURALNETWORKS_NUMBER_PREFERENCES) { + ALOGE("ANeuralNetworksRequest_setPreference invalid preference %u", preference); + return ANEURALNETWORKS_BAD_DATA; + } + + Request* r = reinterpret_cast<Request*>(request); + r->setPreference(preference); + return ANEURALNETWORKS_NO_ERROR; +} + +int ANeuralNetworksRequest_setInput(ANeuralNetworksRequest* request, int32_t index, + const ANeuralNetworksOperandType* type, const void* buffer, + size_t length) { + if (!request || !buffer) { + ALOGE("ANeuralNetworksRequest_setInput passed a nullptr"); + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + if (type != nullptr) { + int n = ValidateOperandType(*type, "ANeuralNetworksRequest_setInput", false); + if (n != ANEURALNETWORKS_NO_ERROR) { + return n; + } + } + if (length > 0xFFFFFFFF) { + ALOGE("ANeuralNetworksRequest_setInput input exceeds max length %zu", length); + } + uint32_t l = static_cast<uint32_t>(length); + Request* r = reinterpret_cast<Request*>(request); + return r->setInput(index, type, buffer, l); +} + +int ANeuralNetworksRequest_setInputFromHardwareBuffer(ANeuralNetworksRequest* request, + int32_t index, + const ANeuralNetworksOperandType* type, + const AHardwareBuffer* buffer) { + if (!request || !type || !buffer) { + ALOGE("ANeuralNetworksRequest_setInputFromHardwareBuffer passed a nullptr"); + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + // TODO validate the rest + + Request* r = reinterpret_cast<Request*>(request); + return r->setInputFromHardwareBuffer(index, type, buffer); +} + +int ANeuralNetworksRequest_setOutput(ANeuralNetworksRequest* request, int32_t index, + const ANeuralNetworksOperandType* type, void* buffer, + size_t length) { + if (!request || !buffer) { + ALOGE("ANeuralNetworksRequest_setOutput passed a nullptr"); + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + if (type != nullptr) { + int n = ValidateOperandType(*type, "ANeuralNetworksRequest_setOutput", false); + if (n != ANEURALNETWORKS_NO_ERROR) { + return n; + } + } + if (length > 0xFFFFFFFF) { + ALOGE("ANeuralNetworksRequest_setOutput input exceeds max length %zu", length); + } + uint32_t l = static_cast<uint32_t>(length); + + Request* r = reinterpret_cast<Request*>(request); + return r->setOutput(index, type, buffer, l); +} + +int ANeuralNetworksRequest_setOutputFromHardwareBuffer(ANeuralNetworksRequest* request, + int32_t index, + const ANeuralNetworksOperandType* type, + const AHardwareBuffer* buffer) { + if (!request || !type || !buffer) { + ALOGE("ANeuralNetworksRequest_setOutputFromHardwareBuffer passed a nullptr"); + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + // TODO validate the rest + + Request* r = reinterpret_cast<Request*>(request); + return r->setOutputFromHardwareBuffer(index, type, buffer); +} + +int ANeuralNetworksRequest_startCompute(ANeuralNetworksRequest* request, + ANeuralNetworksEvent** event) { + if (!request || !event) { + ALOGE("ANeuralNetworksRequest_startCompute passed a nullptr"); + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + // TODO validate the rest + + Request* r = reinterpret_cast<Request*>(request); + Event* e = nullptr; + int n = r->startCompute(&e); + if (n != ANEURALNETWORKS_NO_ERROR) { + return n; + } + *event = reinterpret_cast<ANeuralNetworksEvent*>(e); + return ANEURALNETWORKS_NO_ERROR; +} + +int ANeuralNetworksEvent_wait(ANeuralNetworksEvent* event) { + if (event == nullptr) { + ALOGE("ANeuralNetworksEvent_wait passed a nullptr"); + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + + Event* e = reinterpret_cast<Event*>(event); + e->wait(); + return ANEURALNETWORKS_NO_ERROR; +} + +void ANeuralNetworksEvent_free(ANeuralNetworksEvent* event) { + // No validation. Free of nullptr is valid. + Event* e = reinterpret_cast<Event*>(event); + delete e; +}