You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
259 lines
9.6 KiB
259 lines
9.6 KiB
/*
|
|
* Copyright (C) 2021 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.
|
|
*/
|
|
|
|
#include "CanonicalDevice.h"
|
|
|
|
#include <Tracing.h>
|
|
#include <android-base/logging.h>
|
|
#include <nnapi/IBuffer.h>
|
|
#include <nnapi/IDevice.h>
|
|
#include <nnapi/IPreparedModel.h>
|
|
#include <nnapi/OperandTypes.h>
|
|
#include <nnapi/Result.h>
|
|
#include <nnapi/Types.h>
|
|
#include <nnapi/Validation.h>
|
|
|
|
#include <algorithm>
|
|
#include <any>
|
|
#include <functional>
|
|
#include <iterator>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <set>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "CanonicalBuffer.h"
|
|
#include "CanonicalPreparedModel.h"
|
|
|
|
namespace android::nn::sample {
|
|
namespace {
|
|
|
|
Capabilities makeCapabilities() {
|
|
constexpr float kPerf = 1.0f;
|
|
const Capabilities::PerformanceInfo kPerfInfo = {.execTime = kPerf, .powerUsage = kPerf};
|
|
|
|
constexpr OperandType kOperandsTypes[] = {
|
|
OperandType::FLOAT32,
|
|
OperandType::INT32,
|
|
OperandType::UINT32,
|
|
OperandType::TENSOR_FLOAT32,
|
|
OperandType::TENSOR_INT32,
|
|
OperandType::TENSOR_QUANT8_ASYMM,
|
|
OperandType::BOOL,
|
|
OperandType::TENSOR_QUANT16_SYMM,
|
|
OperandType::TENSOR_FLOAT16,
|
|
OperandType::TENSOR_BOOL8,
|
|
OperandType::FLOAT16,
|
|
OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL,
|
|
OperandType::TENSOR_QUANT16_ASYMM,
|
|
OperandType::TENSOR_QUANT8_SYMM,
|
|
OperandType::TENSOR_QUANT8_ASYMM_SIGNED,
|
|
};
|
|
|
|
std::vector<Capabilities::OperandPerformance> operandPerformance;
|
|
operandPerformance.reserve(std::size(kOperandsTypes));
|
|
std::transform(std::begin(kOperandsTypes), std::end(kOperandsTypes),
|
|
std::back_inserter(operandPerformance), [kPerfInfo](OperandType op) {
|
|
return Capabilities::OperandPerformance{.type = op, .info = kPerfInfo};
|
|
});
|
|
auto table =
|
|
Capabilities::OperandPerformanceTable::create(std::move(operandPerformance)).value();
|
|
|
|
return {.relaxedFloat32toFloat16PerformanceScalar = kPerfInfo,
|
|
.relaxedFloat32toFloat16PerformanceTensor = kPerfInfo,
|
|
.operandPerformance = std::move(table),
|
|
.ifPerformance = kPerfInfo,
|
|
.whilePerformance = kPerfInfo};
|
|
}
|
|
|
|
std::string toString(const Dimensions& dimensions) {
|
|
std::ostringstream oss;
|
|
oss << "[";
|
|
for (size_t i = 0; i < dimensions.size(); ++i) {
|
|
if (i != 0) oss << ", ";
|
|
oss << dimensions[i];
|
|
}
|
|
oss << "]";
|
|
return oss.str();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
Device::Device(std::string name, const IOperationResolver* operationResolver)
|
|
: kName(std::move(name)), kOperationResolver(*operationResolver) {
|
|
CHECK(operationResolver != nullptr);
|
|
initVLogMask();
|
|
}
|
|
|
|
const std::string& Device::getName() const {
|
|
return kName;
|
|
}
|
|
|
|
const std::string& Device::getVersionString() const {
|
|
static const std::string kVersionString = "JUST_AN_EXAMPLE";
|
|
return kVersionString;
|
|
}
|
|
|
|
Version Device::getFeatureLevel() const {
|
|
return Version::ANDROID_S;
|
|
}
|
|
|
|
DeviceType Device::getType() const {
|
|
return DeviceType::CPU;
|
|
}
|
|
|
|
const std::vector<Extension>& Device::getSupportedExtensions() const {
|
|
static const std::vector<Extension> kExtensions = {/* No extensions. */};
|
|
return kExtensions;
|
|
}
|
|
|
|
const Capabilities& Device::getCapabilities() const {
|
|
static const Capabilities kCapabilities = makeCapabilities();
|
|
return kCapabilities;
|
|
}
|
|
|
|
std::pair<uint32_t, uint32_t> Device::getNumberOfCacheFilesNeeded() const {
|
|
return std::make_pair(/*numModelCache=*/0, /*numDataCache=*/0);
|
|
}
|
|
|
|
GeneralResult<void> Device::wait() const {
|
|
return {};
|
|
}
|
|
|
|
GeneralResult<std::vector<bool>> Device::getSupportedOperations(const Model& model) const {
|
|
VLOG(DRIVER) << "sample::Device::getSupportedOperations";
|
|
|
|
// Validate arguments.
|
|
if (const auto result = validate(model); !result.ok()) {
|
|
return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << result.error();
|
|
}
|
|
|
|
// Mark all operations except extension operations as supported.
|
|
std::vector<bool> supported;
|
|
supported.reserve(model.main.operations.size());
|
|
std::transform(model.main.operations.begin(), model.main.operations.end(),
|
|
std::back_inserter(supported), [](const Operation& operation) {
|
|
return !isExtensionOperationType(operation.type) &&
|
|
operation.type != OperationType::OEM_OPERATION;
|
|
});
|
|
|
|
return supported;
|
|
}
|
|
|
|
GeneralResult<SharedPreparedModel> Device::prepareModel(
|
|
const Model& model, ExecutionPreference preference, Priority priority,
|
|
OptionalTimePoint deadline, const std::vector<SharedHandle>& /*modelCache*/,
|
|
const std::vector<SharedHandle>& /*dataCache*/, const CacheToken& /*token*/) const {
|
|
if (VLOG_IS_ON(DRIVER)) {
|
|
VLOG(DRIVER) << "sample::Device::prepareModel";
|
|
logModelToInfo(model);
|
|
}
|
|
|
|
// Validate arguments.
|
|
if (const auto result = validate(model); !result.ok()) {
|
|
return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Invalid Model: " << result.error();
|
|
}
|
|
if (const auto result = validate(preference); !result.ok()) {
|
|
return NN_ERROR(ErrorStatus::INVALID_ARGUMENT)
|
|
<< "Invalid ExecutionPreference: " << result.error();
|
|
}
|
|
if (const auto result = validate(priority); !result.ok()) {
|
|
return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Invalid Priority: " << result.error();
|
|
}
|
|
|
|
// Check if deadline has passed.
|
|
if (hasDeadlinePassed(deadline)) {
|
|
return NN_ERROR(ErrorStatus::MISSED_DEADLINE_PERSISTENT);
|
|
}
|
|
|
|
std::vector<RunTimePoolInfo> poolInfos;
|
|
if (!setRunTimePoolInfosFromCanonicalMemories(&poolInfos, model.pools)) {
|
|
return NN_ERROR() << "setRunTimePoolInfosFromCanonicalMemories failed";
|
|
}
|
|
|
|
// Create the prepared model.
|
|
return std::make_shared<const PreparedModel>(model, preference, priority, &kOperationResolver,
|
|
kBufferTracker, std::move(poolInfos));
|
|
}
|
|
|
|
GeneralResult<SharedPreparedModel> Device::prepareModelFromCache(
|
|
OptionalTimePoint /*deadline*/, const std::vector<SharedHandle>& /*modelCache*/,
|
|
const std::vector<SharedHandle>& /*dataCache*/, const CacheToken& /*token*/) const {
|
|
NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_COMPILATION,
|
|
"sample::Device::prepareModelFromCache");
|
|
return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
|
|
<< "prepareModelFromCache not supported on sample::Device::prepareModelFromCache("
|
|
<< kName << ")";
|
|
}
|
|
|
|
GeneralResult<SharedBuffer> Device::allocate(const BufferDesc& desc,
|
|
const std::vector<SharedPreparedModel>& preparedModels,
|
|
const std::vector<BufferRole>& inputRoles,
|
|
const std::vector<BufferRole>& outputRoles) const {
|
|
VLOG(DRIVER) << "sample::Device::allocate";
|
|
std::set<PreparedModelRole> roles;
|
|
Operand operand;
|
|
auto getModel = [](const SharedPreparedModel& preparedModel) -> const Model* {
|
|
std::any resource = preparedModel->getUnderlyingResource();
|
|
const Model** maybeModel = std::any_cast<const Model*>(&resource);
|
|
if (maybeModel == nullptr) {
|
|
LOG(ERROR) << "sample::Device::allocate -- unknown remote IPreparedModel.";
|
|
return nullptr;
|
|
}
|
|
return *maybeModel;
|
|
};
|
|
if (const auto result = validateMemoryDesc(desc, preparedModels, inputRoles, outputRoles,
|
|
getModel, &roles, &operand);
|
|
!result.ok()) {
|
|
return NN_ERROR(ErrorStatus::INVALID_ARGUMENT)
|
|
<< "sample::Device::allocate -- validation failed: " << result.error();
|
|
}
|
|
|
|
if (isExtensionOperandType(operand.type)) {
|
|
return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
|
|
<< "sample::Device::allocate -- does not support extension type.";
|
|
}
|
|
|
|
// TODO(xusongw): Support allocating buffers with unknown dimensions or rank.
|
|
uint32_t size = nonExtensionOperandSizeOfData(operand.type, operand.dimensions);
|
|
VLOG(DRIVER) << "sample::Device::allocate -- type = " << operand.type
|
|
<< ", dimensions = " << toString(operand.dimensions) << ", size = " << size;
|
|
if (size == 0) {
|
|
return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
|
|
<< "sample::Device::allocate -- does not support dynamic output shape.";
|
|
}
|
|
|
|
auto bufferWrapper = ManagedBuffer::create(size, std::move(roles), operand);
|
|
if (bufferWrapper == nullptr) {
|
|
return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
|
|
<< "sample::Device::allocate -- not enough memory.";
|
|
}
|
|
|
|
auto token = kBufferTracker->add(bufferWrapper);
|
|
if (token == nullptr) {
|
|
return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
|
|
<< "sample::Device::allocate -- BufferTracker returned invalid token.";
|
|
}
|
|
|
|
auto sampleBuffer = std::make_shared<const Buffer>(std::move(bufferWrapper), std::move(token));
|
|
VLOG(DRIVER) << "sample::Device::allocate -- successfully allocates the requested memory";
|
|
return sampleBuffer;
|
|
}
|
|
|
|
} // namespace android::nn::sample
|