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.
642 lines
28 KiB
642 lines
28 KiB
/*
|
|
* 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.
|
|
*/
|
|
|
|
#define LOG_TAG "SampleDriver"
|
|
|
|
#include "SampleDriver.h"
|
|
|
|
#include <android-base/logging.h>
|
|
#include <android-base/properties.h>
|
|
#include <android-base/scopeguard.h>
|
|
#include <android/binder_auto_utils.h>
|
|
#include <android/binder_interface_utils.h>
|
|
#include <android/binder_manager.h>
|
|
#include <android/binder_process.h>
|
|
#include <nnapi/Result.h>
|
|
#include <nnapi/Types.h>
|
|
#include <nnapi/Validation.h>
|
|
#include <nnapi/hal/aidl/Conversions.h>
|
|
#include <nnapi/hal/aidl/Utils.h>
|
|
|
|
#include <algorithm>
|
|
#include <chrono>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <set>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <tuple>
|
|
#include <utility>
|
|
#include <variant>
|
|
#include <vector>
|
|
|
|
#include "AidlBufferTracker.h"
|
|
#include "AidlHalUtils.h"
|
|
#include "CpuExecutor.h"
|
|
#include "SampleDriverUtils.h"
|
|
#include "Tracing.h"
|
|
#include "Utils.h"
|
|
|
|
namespace android {
|
|
namespace nn {
|
|
namespace sample_driver {
|
|
|
|
namespace {
|
|
|
|
int64_t nanosecondsDuration(TimePoint end, TimePoint start) {
|
|
return std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
|
|
};
|
|
|
|
constexpr aidl_hal::Timing kNoTiming = {.timeOnDeviceNs = -1, .timeInDriverNs = -1};
|
|
|
|
} // namespace
|
|
|
|
ndk::ScopedAStatus SampleDriver::getVersionString(std::string* versionString) {
|
|
NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INITIALIZATION,
|
|
"SampleDriver::getVersionString");
|
|
*versionString = "JUST_AN_EXAMPLE";
|
|
return ndk::ScopedAStatus::ok();
|
|
}
|
|
|
|
ndk::ScopedAStatus SampleDriver::getType(aidl_hal::DeviceType* deviceType) {
|
|
NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INITIALIZATION, "SampleDriver::getType");
|
|
*deviceType = aidl_hal::DeviceType::CPU;
|
|
return ndk::ScopedAStatus::ok();
|
|
}
|
|
|
|
ndk::ScopedAStatus SampleDriver::getSupportedExtensions(
|
|
std::vector<aidl_hal::Extension>* supportedExtensions) {
|
|
NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INITIALIZATION,
|
|
"SampleDriver::getSupportedExtensions");
|
|
*supportedExtensions = {/* No extensions. */};
|
|
return ndk::ScopedAStatus::ok();
|
|
}
|
|
|
|
ndk::ScopedAStatus SampleDriver::getNumberOfCacheFilesNeeded(
|
|
aidl_hal::NumberOfCacheFiles* numberOfCacheFiles) {
|
|
NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INITIALIZATION,
|
|
"SampleDriver::getNumberOfCacheFilesNeeded");
|
|
// Set both numbers to be 0 for cache not supported.
|
|
numberOfCacheFiles->numDataCache = 0;
|
|
numberOfCacheFiles->numModelCache = 0;
|
|
return ndk::ScopedAStatus::ok();
|
|
}
|
|
|
|
ndk::ScopedAStatus SampleDriver::prepareModel(
|
|
const aidl_hal::Model& model, aidl_hal::ExecutionPreference preference,
|
|
aidl_hal::Priority priority, int64_t deadlineNs,
|
|
const std::vector<ndk::ScopedFileDescriptor>& /*modelCache*/,
|
|
const std::vector<ndk::ScopedFileDescriptor>& /*dataCache*/,
|
|
const std::vector<uint8_t>& /*token*/,
|
|
const std::shared_ptr<aidl_hal::IPreparedModelCallback>& callback) {
|
|
NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_COMPILATION, "SampleDriver::prepareModel");
|
|
auto copiedModel = aidl_hal::utils::clone(model);
|
|
if (!copiedModel.has_value()) {
|
|
return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE, copiedModel.error().message);
|
|
}
|
|
return prepareModelBase(std::move(copiedModel).value(), this, preference, priority, deadlineNs,
|
|
callback);
|
|
}
|
|
|
|
ndk::ScopedAStatus SampleDriver::prepareModelFromCache(
|
|
int64_t /*deadlineNs*/, const std::vector<ndk::ScopedFileDescriptor>& /*modelCache*/,
|
|
const std::vector<ndk::ScopedFileDescriptor>& /*dataCache*/,
|
|
const std::vector<uint8_t>& /*token*/,
|
|
const std::shared_ptr<aidl_hal::IPreparedModelCallback>& callback) {
|
|
NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_COMPILATION,
|
|
"SampleDriver::prepareModelFromCache");
|
|
notify(callback, aidl_hal::ErrorStatus::GENERAL_FAILURE, nullptr);
|
|
return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
|
|
"Caching is not supported in the sample driver.");
|
|
}
|
|
|
|
// Safely downcast an IPreparedModel object to SamplePreparedModel.
|
|
// This function will return nullptr if the IPreparedModel object is not originated from the sample
|
|
// driver process.
|
|
static const SamplePreparedModel* castToSamplePreparedModel(
|
|
const std::shared_ptr<aidl_hal::IPreparedModel>& preparedModel) {
|
|
if (preparedModel->isRemote()) {
|
|
return nullptr;
|
|
} else {
|
|
// This static_cast is safe because SamplePreparedModel is the only class that implements
|
|
// the IPreparedModel interface in the sample driver process.
|
|
return static_cast<const SamplePreparedModel*>(preparedModel.get());
|
|
}
|
|
}
|
|
|
|
ndk::ScopedAStatus SampleDriver::allocate(
|
|
const aidl_hal::BufferDesc& desc,
|
|
const std::vector<aidl_hal::IPreparedModelParcel>& halPreparedModels,
|
|
const std::vector<aidl_hal::BufferRole>& inputRoles,
|
|
const std::vector<aidl_hal::BufferRole>& outputRoles, aidl_hal::DeviceBuffer* buffer) {
|
|
VLOG(DRIVER) << "SampleDriver::allocate";
|
|
constexpr auto getModel = [](const std::shared_ptr<aidl_hal::IPreparedModel>& preparedModel)
|
|
-> const aidl_hal::Model* {
|
|
const auto* samplePreparedModel = castToSamplePreparedModel(preparedModel);
|
|
if (samplePreparedModel == nullptr) {
|
|
LOG(ERROR) << "SampleDriver::allocate -- unknown remote IPreparedModel.";
|
|
return nullptr;
|
|
}
|
|
return samplePreparedModel->getModel();
|
|
};
|
|
|
|
std::vector<std::shared_ptr<aidl_hal::IPreparedModel>> preparedModels;
|
|
preparedModels.reserve(halPreparedModels.size());
|
|
for (const auto& halPreparedModelParcel : halPreparedModels) {
|
|
preparedModels.push_back(halPreparedModelParcel.preparedModel);
|
|
}
|
|
std::set<AidlHalPreparedModelRole> roles;
|
|
aidl_hal::Operand operand;
|
|
if (!validateMemoryDesc(desc, preparedModels, inputRoles, outputRoles, getModel, &roles,
|
|
&operand)) {
|
|
LOG(ERROR) << "SampleDriver::allocate -- validation failed.";
|
|
return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
|
|
"SampleDriver::allocate -- validation failed.");
|
|
}
|
|
|
|
if (isExtensionOperandType(operand.type)) {
|
|
LOG(ERROR) << "SampleDriver::allocate -- does not support extension type.";
|
|
return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
|
|
"SampleDriver::allocate -- does not support extension type.");
|
|
}
|
|
|
|
// TODO(xusongw): Support allocating buffers with unknown dimensions or rank.
|
|
|
|
// An operand obtained from validateMemoryDesc is guaranteed to be representable in canonical
|
|
// types.
|
|
uint32_t size = nonExtensionOperandSizeOfData(convert(operand.type).value(),
|
|
toUnsigned(operand.dimensions).value());
|
|
VLOG(DRIVER) << "SampleDriver::allocate -- type = " << toString(operand.type)
|
|
<< ", dimensions = " << toString(operand.dimensions) << ", size = " << size;
|
|
if (size == 0) {
|
|
LOG(ERROR) << "SampleDriver::allocate -- does not support dynamic output shape.";
|
|
return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
|
|
"SampleDriver::allocate -- does not support dynamic output shape.");
|
|
}
|
|
|
|
// An operand obtained from validateMemoryDesc is guaranteed to be representable in canonical
|
|
// types, so it safe to do an unvalidated conversion here.
|
|
auto bufferWrapper =
|
|
AidlManagedBuffer::create(size, std::move(roles), unvalidatedConvert(operand).value());
|
|
if (bufferWrapper == nullptr) {
|
|
LOG(ERROR) << "SampleDriver::allocate -- not enough memory.";
|
|
return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
|
|
"SampleDriver::allocate -- not enough memory.");
|
|
}
|
|
|
|
auto token = mBufferTracker->add(bufferWrapper);
|
|
if (token == nullptr) {
|
|
LOG(ERROR) << "SampleDriver::allocate -- AidlBufferTracker returned invalid token.";
|
|
return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
|
|
"SampleDriver::allocate -- AidlBufferTracker returned invalid token.");
|
|
}
|
|
|
|
const uint32_t tokenValue = token->get();
|
|
std::shared_ptr<SampleBuffer> sampleBuffer =
|
|
ndk::SharedRefBase::make<SampleBuffer>(std::move(bufferWrapper), std::move(token));
|
|
VLOG(DRIVER) << "SampleDriver::allocate -- successfully allocates the requested memory";
|
|
buffer->buffer = std::move(sampleBuffer);
|
|
buffer->token = tokenValue;
|
|
return ndk::ScopedAStatus::ok();
|
|
}
|
|
|
|
int SampleDriver::run() {
|
|
ABinderProcess_setThreadPoolMaxThreadCount(4);
|
|
const std::string name = std::string(SampleDriver::descriptor) + "/" + mName;
|
|
const binder_status_t status = AServiceManager_addService(this->asBinder().get(), name.c_str());
|
|
if (status != STATUS_OK) {
|
|
return 1;
|
|
}
|
|
ABinderProcess_joinThreadPool();
|
|
return 1;
|
|
}
|
|
|
|
static void copyRunTimePoolInfos(const RunTimePoolInfo& srcPool, const RunTimePoolInfo& dstPool) {
|
|
CHECK(srcPool.getBuffer() != nullptr);
|
|
CHECK(dstPool.getBuffer() != nullptr);
|
|
CHECK(srcPool.getSize() == dstPool.getSize());
|
|
std::copy(srcPool.getBuffer(), srcPool.getBuffer() + srcPool.getSize(), dstPool.getBuffer());
|
|
dstPool.flush();
|
|
}
|
|
|
|
ndk::ScopedAStatus SampleBuffer::copyTo(const aidl_hal::Memory& dst) {
|
|
const auto canonicalMemory = convert(dst);
|
|
if (!canonicalMemory.has_value()) {
|
|
return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, canonicalMemory.error().message);
|
|
}
|
|
const auto dstPool = RunTimePoolInfo::createFromMemory(canonicalMemory.value());
|
|
if (!dstPool.has_value()) {
|
|
LOG(ERROR) << "SampleBuffer::copyTo -- unable to map dst memory.";
|
|
return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
|
|
"SampleBuffer::copyTo -- unable to map dst memory.");
|
|
}
|
|
const auto validationStatus =
|
|
aidl_hal::utils::convert(kBuffer->validateCopyTo(dstPool->getSize())).value();
|
|
if (validationStatus != aidl_hal::ErrorStatus::NONE) {
|
|
return toAStatus(validationStatus);
|
|
}
|
|
const auto srcPool = kBuffer->createRunTimePoolInfo();
|
|
copyRunTimePoolInfos(srcPool, dstPool.value());
|
|
return ndk::ScopedAStatus::ok();
|
|
}
|
|
|
|
static aidl_hal::ErrorStatus copyFromInternal(
|
|
const aidl_hal::Memory& src, const std::vector<uint32_t>& dimensions,
|
|
const std::shared_ptr<AidlManagedBuffer>& bufferWrapper) {
|
|
CHECK(bufferWrapper != nullptr);
|
|
const auto canonicalMemory = convert(src);
|
|
if (!canonicalMemory.has_value()) {
|
|
return aidl_hal::ErrorStatus::INVALID_ARGUMENT;
|
|
}
|
|
const auto srcPool = RunTimePoolInfo::createFromMemory(canonicalMemory.value());
|
|
if (!srcPool.has_value()) {
|
|
LOG(ERROR) << "SampleBuffer::copyFrom -- unable to map src memory.";
|
|
return aidl_hal::ErrorStatus::GENERAL_FAILURE;
|
|
}
|
|
const auto validationStatus = aidl_hal::utils::convert(bufferWrapper->validateCopyFrom(
|
|
dimensions, srcPool->getSize()))
|
|
.value();
|
|
if (validationStatus != aidl_hal::ErrorStatus::NONE) {
|
|
return validationStatus;
|
|
}
|
|
const auto dstPool = bufferWrapper->createRunTimePoolInfo();
|
|
copyRunTimePoolInfos(srcPool.value(), dstPool);
|
|
return aidl_hal::ErrorStatus::NONE;
|
|
}
|
|
|
|
ndk::ScopedAStatus SampleBuffer::copyFrom(const aidl_hal::Memory& src,
|
|
const std::vector<int32_t>& dimensions) {
|
|
const auto unsignedDimensions = toUnsigned(dimensions);
|
|
if (!unsignedDimensions.has_value()) {
|
|
return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
|
|
unsignedDimensions.error().message);
|
|
}
|
|
const auto status = copyFromInternal(src, unsignedDimensions.value(), kBuffer);
|
|
if (status != aidl_hal::ErrorStatus::NONE) {
|
|
kBuffer->setInitialized(false);
|
|
return toAStatus(status);
|
|
}
|
|
kBuffer->updateDimensions(unsignedDimensions.value());
|
|
kBuffer->setInitialized(true);
|
|
return ndk::ScopedAStatus::ok();
|
|
}
|
|
|
|
bool SamplePreparedModel::initialize() {
|
|
const auto canonicalPools = convert(mModel.pools);
|
|
if (!canonicalPools.has_value()) {
|
|
return false;
|
|
}
|
|
return setRunTimePoolInfosFromCanonicalMemories(&mPoolInfos, canonicalPools.value());
|
|
}
|
|
|
|
static std::tuple<aidl_hal::ErrorStatus, std::vector<RunTimePoolInfo>,
|
|
std::vector<std::shared_ptr<AidlManagedBuffer>>>
|
|
createRunTimePoolInfos(const Request& request, const SampleDriver& driver,
|
|
const SamplePreparedModel* preparedModel) {
|
|
std::vector<RunTimePoolInfo> requestPoolInfos;
|
|
std::vector<std::shared_ptr<AidlManagedBuffer>> bufferWrappers;
|
|
requestPoolInfos.reserve(request.pools.size());
|
|
bufferWrappers.reserve(request.pools.size());
|
|
for (uint32_t i = 0; i < request.pools.size(); i++) {
|
|
const auto& pool = request.pools[i];
|
|
if (const auto* memory = std::get_if<SharedMemory>(&pool)) {
|
|
auto buffer = RunTimePoolInfo::createFromMemory(*memory);
|
|
if (!buffer.has_value()) {
|
|
LOG(ERROR) << "createRuntimeMemoriesFromMemoryPools -- could not map pools";
|
|
return {aidl_hal::ErrorStatus::GENERAL_FAILURE, {}, {}};
|
|
}
|
|
requestPoolInfos.push_back(std::move(*buffer));
|
|
bufferWrappers.push_back(nullptr);
|
|
} else if (const auto* token = std::get_if<Request::MemoryDomainToken>(&pool)) {
|
|
auto bufferWrapper = driver.getBufferTracker()->get(static_cast<uint32_t>(*token));
|
|
if (bufferWrapper == nullptr) {
|
|
return {aidl_hal::ErrorStatus::INVALID_ARGUMENT, {}, {}};
|
|
}
|
|
const auto validationStatus =
|
|
aidl_hal::utils::convert(
|
|
bufferWrapper->validateRequest(i, request, preparedModel))
|
|
.value();
|
|
if (validationStatus != aidl_hal::ErrorStatus::NONE) {
|
|
return {validationStatus, {}, {}};
|
|
}
|
|
requestPoolInfos.push_back(bufferWrapper->createRunTimePoolInfo());
|
|
bufferWrappers.push_back(std::move(bufferWrapper));
|
|
} else {
|
|
// If the pool is not a Memory or a token, the input is invalid.
|
|
return {aidl_hal::ErrorStatus::INVALID_ARGUMENT, {}, {}};
|
|
}
|
|
}
|
|
return {aidl_hal::ErrorStatus::NONE, std::move(requestPoolInfos), std::move(bufferWrappers)};
|
|
}
|
|
|
|
static aidl_hal::ErrorStatus updateDeviceMemories(
|
|
aidl_hal::ErrorStatus status, const Request& request,
|
|
const std::vector<std::shared_ptr<AidlManagedBuffer>>& bufferWrappers,
|
|
const std::vector<aidl_hal::OutputShape>& outputShapes) {
|
|
if (status == aidl_hal::ErrorStatus::NONE) {
|
|
for (uint32_t i = 0; i < request.outputs.size(); i++) {
|
|
const uint32_t poolIndex = request.outputs[i].location.poolIndex;
|
|
const auto& pool = request.pools[poolIndex];
|
|
if (std::holds_alternative<Request::MemoryDomainToken>(pool)) {
|
|
const auto unsignedDimensions = toUnsigned(outputShapes[i].dimensions).value();
|
|
if (!bufferWrappers[poolIndex]->updateDimensions(unsignedDimensions)) {
|
|
return aidl_hal::ErrorStatus::GENERAL_FAILURE;
|
|
}
|
|
}
|
|
}
|
|
for (uint32_t i = 0; i < request.outputs.size(); i++) {
|
|
const uint32_t poolIndex = request.outputs[i].location.poolIndex;
|
|
const auto& pool = request.pools[poolIndex];
|
|
if (std::holds_alternative<Request::MemoryDomainToken>(pool)) {
|
|
bufferWrappers[poolIndex]->setInitialized(true);
|
|
}
|
|
}
|
|
} else if (status == aidl_hal::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
|
|
// If CpuExecutor reports OUTPUT_INSUFFCIENT_SIZE on a device memory, this is because the
|
|
// dimensions of the device memory are incorrectly specified. The driver should return
|
|
// GENERAL_FAILURE instead in this case.
|
|
for (uint32_t i = 0; i < request.outputs.size(); i++) {
|
|
const uint32_t poolIndex = request.outputs[i].location.poolIndex;
|
|
const auto& pool = request.pools[poolIndex];
|
|
if (std::holds_alternative<Request::MemoryDomainToken>(pool)) {
|
|
if (!outputShapes[i].isSufficient) {
|
|
LOG(ERROR) << "Invalid dimensions for output " << i
|
|
<< ": actual shape = " << toString(outputShapes[i].dimensions);
|
|
return aidl_hal::ErrorStatus::GENERAL_FAILURE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return aidl_hal::ErrorStatus::NONE;
|
|
}
|
|
|
|
ndk::ScopedAStatus SamplePreparedModel::executeSynchronously(
|
|
const aidl_hal::Request& halRequest, bool measureTiming, int64_t halDeadlineNs,
|
|
int64_t loopTimeoutDurationNs, aidl_hal::ExecutionResult* executionResult) {
|
|
NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION,
|
|
"SampleDriver::executeSynchronously");
|
|
VLOG(DRIVER) << "executeSynchronously(" << SHOW_IF_DEBUG(halRequest.toString()) << ")";
|
|
|
|
TimePoint driverStart, driverEnd, deviceStart, deviceEnd;
|
|
if (measureTiming) driverStart = Clock::now();
|
|
|
|
const auto model = convert(mModel).value();
|
|
|
|
auto maybeRequest = convert(halRequest);
|
|
if (!maybeRequest.has_value()) {
|
|
return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, maybeRequest.error().message);
|
|
}
|
|
const auto request = std::move(maybeRequest).value();
|
|
|
|
const auto validationResult = validateRequestForModel(request, model);
|
|
if (!validationResult.ok()) {
|
|
return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, validationResult.error());
|
|
}
|
|
|
|
if (halDeadlineNs < -1) {
|
|
return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
|
|
"Invalid deadline: " + toString(halDeadlineNs));
|
|
}
|
|
if (loopTimeoutDurationNs < -1) {
|
|
return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
|
|
"Invalid loop timeout duration: " + toString(loopTimeoutDurationNs));
|
|
}
|
|
|
|
const auto deadline = makeDeadline(halDeadlineNs);
|
|
if (hasDeadlinePassed(deadline)) {
|
|
return toAStatus(aidl_hal::ErrorStatus::MISSED_DEADLINE_PERSISTENT);
|
|
}
|
|
|
|
NNTRACE_FULL_SWITCH(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INPUTS_AND_OUTPUTS,
|
|
"SampleDriver::executeSynchronouslyBase");
|
|
const auto [poolStatus, requestPoolInfos, bufferWrappers] =
|
|
createRunTimePoolInfos(request, *mDriver, this);
|
|
if (poolStatus != aidl_hal::ErrorStatus::NONE) {
|
|
return toAStatus(poolStatus);
|
|
}
|
|
|
|
NNTRACE_FULL_SWITCH(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION,
|
|
"SampleDriver::executeSynchronouslyBase");
|
|
CpuExecutor executor = mDriver->getExecutor();
|
|
if (loopTimeoutDurationNs >= 0) {
|
|
executor.setLoopTimeout(loopTimeoutDurationNs);
|
|
}
|
|
if (deadline.has_value()) {
|
|
executor.setDeadline(*deadline);
|
|
}
|
|
if (measureTiming) deviceStart = Clock::now();
|
|
int n = executor.run(model, request, mPoolInfos, requestPoolInfos);
|
|
if (measureTiming) deviceEnd = Clock::now();
|
|
VLOG(DRIVER) << "executor.run returned " << n;
|
|
aidl_hal::ErrorStatus executionStatus = convertResultCodeToAidlErrorStatus(n);
|
|
if (executionStatus != aidl_hal::ErrorStatus::NONE &&
|
|
executionStatus != aidl_hal::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
|
|
return toAStatus(executionStatus);
|
|
}
|
|
auto outputShapes = aidl_hal::utils::convert(executor.getOutputShapes()).value();
|
|
|
|
// Update device memory metadata.
|
|
const aidl_hal::ErrorStatus updateStatus =
|
|
updateDeviceMemories(executionStatus, request, bufferWrappers, outputShapes);
|
|
if (updateStatus != aidl_hal::ErrorStatus::NONE) {
|
|
return toAStatus(updateStatus);
|
|
}
|
|
|
|
executionResult->outputSufficientSize =
|
|
executionStatus != aidl_hal::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
|
|
executionResult->outputShapes = std::move(outputShapes);
|
|
executionResult->timing = kNoTiming;
|
|
if (measureTiming && executionStatus == aidl_hal::ErrorStatus::NONE) {
|
|
driverEnd = Clock::now();
|
|
aidl_hal::Timing timing = {.timeOnDeviceNs = nanosecondsDuration(deviceEnd, deviceStart),
|
|
.timeInDriverNs = nanosecondsDuration(driverEnd, driverStart)};
|
|
VLOG(DRIVER) << "executeSynchronously timing = " << timing.toString();
|
|
|
|
executionResult->timing = timing;
|
|
}
|
|
return ndk::ScopedAStatus::ok();
|
|
}
|
|
|
|
// The sample driver will finish the execution and then return.
|
|
ndk::ScopedAStatus SamplePreparedModel::executeFenced(
|
|
const aidl_hal::Request& halRequest, const std::vector<ndk::ScopedFileDescriptor>& waitFor,
|
|
bool measureTiming, int64_t halDeadlineNs, int64_t loopTimeoutDurationNs,
|
|
int64_t durationNs, aidl_hal::FencedExecutionResult* executionResult) {
|
|
NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION,
|
|
"SamplePreparedModel::executeFenced");
|
|
VLOG(DRIVER) << "executeFenced(" << SHOW_IF_DEBUG(halRequest.toString()) << ")";
|
|
|
|
TimePoint driverStart, driverEnd, deviceStart, deviceEnd;
|
|
if (measureTiming) driverStart = Clock::now();
|
|
|
|
const auto model = convert(mModel).value();
|
|
|
|
auto maybeRequest = convert(halRequest);
|
|
if (!maybeRequest.has_value()) {
|
|
return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, maybeRequest.error().message);
|
|
}
|
|
const auto request = std::move(maybeRequest).value();
|
|
|
|
const auto validationResult =
|
|
validateRequestForModel(request, model, /*allowUnspecifiedOutput=*/false);
|
|
if (!validationResult.ok()) {
|
|
return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, validationResult.error());
|
|
}
|
|
|
|
if (halDeadlineNs < -1) {
|
|
return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
|
|
"Invalid deadline: " + toString(halDeadlineNs));
|
|
}
|
|
if (loopTimeoutDurationNs < -1) {
|
|
return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
|
|
"Invalid loop timeout duration: " + toString(loopTimeoutDurationNs));
|
|
}
|
|
if (durationNs < -1) {
|
|
return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
|
|
"Invalid fenced execution duration: " + toString(durationNs));
|
|
}
|
|
|
|
const auto deadline = makeDeadline(halDeadlineNs);
|
|
if (hasDeadlinePassed(deadline)) {
|
|
return toAStatus(aidl_hal::ErrorStatus::MISSED_DEADLINE_PERSISTENT);
|
|
}
|
|
|
|
// Wait for the dependent events to signal
|
|
for (const auto& fenceHandle : waitFor) {
|
|
int syncFenceFd = fenceHandle.get();
|
|
if (syncWait(syncFenceFd, -1) != FenceState::SIGNALED) {
|
|
LOG(ERROR) << "syncWait failed";
|
|
return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE, "syncWait failed");
|
|
}
|
|
}
|
|
|
|
// Update deadline if the timeout duration is closer than the deadline.
|
|
auto closestDeadline = deadline;
|
|
if (durationNs >= 0) {
|
|
const auto timeoutDurationDeadline = makeDeadline(durationNs);
|
|
if (!closestDeadline.has_value() || *closestDeadline > timeoutDurationDeadline) {
|
|
closestDeadline = timeoutDurationDeadline;
|
|
}
|
|
}
|
|
|
|
TimePoint driverStartAfterFence;
|
|
if (measureTiming) driverStartAfterFence = Clock::now();
|
|
|
|
NNTRACE_FULL_SWITCH(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INPUTS_AND_OUTPUTS,
|
|
"SamplePreparedModel::executeFenced");
|
|
const auto [poolStatus, requestPoolInfos, bufferWrappers] =
|
|
createRunTimePoolInfos(request, *mDriver, this);
|
|
if (poolStatus != aidl_hal::ErrorStatus::NONE) {
|
|
return toAStatus(poolStatus);
|
|
}
|
|
|
|
NNTRACE_FULL_SWITCH(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION,
|
|
"SamplePreparedModel::executeFenced");
|
|
CpuExecutor executor = mDriver->getExecutor();
|
|
if (loopTimeoutDurationNs >= 0) {
|
|
executor.setLoopTimeout(loopTimeoutDurationNs);
|
|
}
|
|
if (closestDeadline.has_value()) {
|
|
executor.setDeadline(*closestDeadline);
|
|
}
|
|
if (measureTiming) deviceStart = Clock::now();
|
|
int n = executor.run(model, request, mPoolInfos, requestPoolInfos);
|
|
if (measureTiming) deviceEnd = Clock::now();
|
|
VLOG(DRIVER) << "executor.run returned " << n;
|
|
aidl_hal::ErrorStatus executionStatus = convertResultCodeToAidlErrorStatus(n);
|
|
if (executionStatus != aidl_hal::ErrorStatus::NONE) {
|
|
return toAStatus(executionStatus);
|
|
}
|
|
|
|
// Set output memories to the initialized state.
|
|
if (executionStatus == aidl_hal::ErrorStatus::NONE) {
|
|
for (const auto& output : request.outputs) {
|
|
const uint32_t poolIndex = output.location.poolIndex;
|
|
const auto& pool = request.pools[poolIndex];
|
|
if (std::holds_alternative<Request::MemoryDomainToken>(pool)) {
|
|
bufferWrappers[poolIndex]->setInitialized(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
aidl_hal::Timing timingSinceLaunch = kNoTiming;
|
|
aidl_hal::Timing timingAfterFence = kNoTiming;
|
|
if (measureTiming) {
|
|
driverEnd = Clock::now();
|
|
timingSinceLaunch = {.timeOnDeviceNs = nanosecondsDuration(deviceEnd, deviceStart),
|
|
.timeInDriverNs = nanosecondsDuration(driverEnd, driverStart)};
|
|
timingAfterFence = {
|
|
.timeOnDeviceNs = nanosecondsDuration(deviceEnd, deviceStart),
|
|
.timeInDriverNs = nanosecondsDuration(driverEnd, driverStartAfterFence)};
|
|
VLOG(DRIVER) << "executeFenced timingSinceLaunch = " << timingSinceLaunch.toString();
|
|
VLOG(DRIVER) << "executeFenced timingAfterFence = " << timingAfterFence.toString();
|
|
}
|
|
|
|
executionResult->callback = ndk::SharedRefBase::make<SampleFencedExecutionCallback>(
|
|
timingSinceLaunch, timingAfterFence, executionStatus);
|
|
executionResult->syncFence = ndk::ScopedFileDescriptor();
|
|
return ndk::ScopedAStatus::ok();
|
|
}
|
|
|
|
ndk::ScopedAStatus SamplePreparedModel::configureExecutionBurst(
|
|
std::shared_ptr<aidl_hal::IBurst>* burst) {
|
|
std::shared_ptr<SamplePreparedModel> self = this->template ref<SamplePreparedModel>();
|
|
*burst = ndk::SharedRefBase::make<SampleBurst>(std::move(self));
|
|
return ndk::ScopedAStatus::ok();
|
|
}
|
|
|
|
SampleBurst::SampleBurst(std::shared_ptr<SamplePreparedModel> preparedModel)
|
|
: kPreparedModel(std::move(preparedModel)) {
|
|
CHECK(kPreparedModel != nullptr);
|
|
}
|
|
|
|
ndk::ScopedAStatus SampleBurst::executeSynchronously(
|
|
const aidl_hal::Request& request, const std::vector<int64_t>& memoryIdentifierTokens,
|
|
bool measureTiming, int64_t deadlineNs, int64_t loopTimeoutDurationNs,
|
|
aidl_hal::ExecutionResult* executionResult) {
|
|
if (request.pools.size() != memoryIdentifierTokens.size()) {
|
|
return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
|
|
"request.pools.size() != memoryIdentifierTokens.size()");
|
|
}
|
|
if (!std::all_of(memoryIdentifierTokens.begin(), memoryIdentifierTokens.end(),
|
|
[](int64_t token) { return token >= -1; })) {
|
|
return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, "Invalid memoryIdentifierTokens");
|
|
}
|
|
|
|
// Ensure at most one execution is in flight at a time.
|
|
const bool executionAlreadyInFlight = mExecutionInFlight.test_and_set();
|
|
if (executionAlreadyInFlight) {
|
|
return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
|
|
"Burst object supports at most one execution at a time");
|
|
}
|
|
const auto guard = base::make_scope_guard([this] { mExecutionInFlight.clear(); });
|
|
|
|
return kPreparedModel->executeSynchronously(request, measureTiming, deadlineNs,
|
|
loopTimeoutDurationNs, executionResult);
|
|
}
|
|
|
|
ndk::ScopedAStatus SampleBurst::releaseMemoryResource(int64_t memoryIdentifierToken) {
|
|
if (memoryIdentifierToken < -1) {
|
|
return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, "Invalid memoryIdentifierToken");
|
|
}
|
|
return ndk::ScopedAStatus::ok();
|
|
}
|
|
|
|
} // namespace sample_driver
|
|
} // namespace nn
|
|
} // namespace android
|