/* * Copyright (C) 2020 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 "PreparedModel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface // lifetimes across processes and for protecting asynchronous calls across HIDL. namespace android::hardware::neuralnetworks::adapter { namespace { template auto convertInput(const Type& object) -> decltype(nn::convert(std::declval())) { auto result = nn::convert(object); if (!result.has_value()) { result.error().code = nn::ErrorStatus::INVALID_ARGUMENT; } return result; } class FencedExecutionCallback final : public V1_3::IFencedExecutionCallback { public: explicit FencedExecutionCallback(const nn::ExecuteFencedInfoCallback& callback) : kCallback(callback) { CHECK(callback != nullptr); } Return getExecutionInfo(getExecutionInfo_cb cb) override { const auto result = kCallback(); if (!result.has_value()) { const auto& [message, code] = result.error(); const auto status = V1_3::utils::convert(code).value_or(V1_3::ErrorStatus::GENERAL_FAILURE); LOG(ERROR) << message; cb(status, V1_2::utils::kNoTiming, V1_2::utils::kNoTiming); return Void(); } const auto [timingLaunched, timingFenced] = result.value(); const auto hidlTimingLaunched = V1_3::utils::convert(timingLaunched).value(); const auto hidlTimingFenced = V1_3::utils::convert(timingFenced).value(); cb(V1_3::ErrorStatus::NONE, hidlTimingLaunched, hidlTimingFenced); return Void(); } private: const nn::ExecuteFencedInfoCallback kCallback; }; using ExecutionResult = nn::ExecutionResult, nn::Timing>>; void notify(V1_0::IExecutionCallback* callback, nn::ErrorStatus status, const std::vector& /*outputShapes*/, const nn::Timing& /*timing*/) { if (callback != nullptr) { const auto hidlStatus = V1_0::utils::convert(status).value(); const auto ret = callback->notify(hidlStatus); if (!ret.isOk()) { LOG(ERROR) << "V1_0::IExecutionCallback::notify failed with " << ret.description(); } } } void notify(V1_2::IExecutionCallback* callback, nn::ErrorStatus status, const std::vector& outputShapes, const nn::Timing& timing) { if (callback != nullptr) { const auto hidlStatus = V1_2::utils::convert(status).value(); const auto hidlOutputShapes = V1_2::utils::convert(outputShapes).value(); const auto hidlTiming = V1_2::utils::convert(timing).value(); const auto ret = callback->notify_1_2(hidlStatus, hidlOutputShapes, hidlTiming); if (!ret.isOk()) { LOG(ERROR) << "V1_2::IExecutionCallback::notify_1_2 failed with " << ret.description(); } } } void notify(V1_3::IExecutionCallback* callback, nn::ErrorStatus status, const std::vector& outputShapes, const nn::Timing& timing) { if (callback != nullptr) { const auto hidlStatus = V1_3::utils::convert(status).value(); const auto hidlOutputShapes = V1_3::utils::convert(outputShapes).value(); const auto hidlTiming = V1_3::utils::convert(timing).value(); const auto ret = callback->notify_1_3(hidlStatus, hidlOutputShapes, hidlTiming); if (!ret.isOk()) { LOG(ERROR) << "V1_3::IExecutionCallback::notify_1_3 failed with " << ret.description(); } } } template void notify(CallbackType* callback, ExecutionResult result) { if (!result.has_value()) { const auto [message, status, outputShapes] = std::move(result).error(); LOG(ERROR) << message; notify(callback, status, outputShapes, {}); } else { const auto [outputShapes, timing] = std::move(result).value(); notify(callback, nn::ErrorStatus::NONE, outputShapes, timing); } } nn::GeneralResult execute(const nn::SharedPreparedModel& preparedModel, uid_t userId, const Executor& executor, const V1_0::Request& request, const sp& callback) { if (callback.get() == nullptr) { return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback"; } auto nnRequest = NN_TRY(convertInput(request)); const std::any resource = preparedModel->getUnderlyingResource(); if (const auto* model = std::any_cast(&resource)) { CHECK(*model != nullptr); NN_TRY(utils::makeGeneralFailure(nn::validateRequestForModel(nnRequest, **model), nn::ErrorStatus::INVALID_ARGUMENT)); } Task task = [preparedModel, nnRequest = std::move(nnRequest), callback] { auto result = preparedModel->execute(nnRequest, nn::MeasureTiming::NO, {}, {}); notify(callback.get(), std::move(result)); }; executor(std::move(task), userId, {}); return {}; } nn::GeneralResult execute_1_2(const nn::SharedPreparedModel& preparedModel, uid_t userId, const Executor& executor, const V1_0::Request& request, V1_2::MeasureTiming measure, const sp& callback) { if (callback.get() == nullptr) { return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback"; } auto nnRequest = NN_TRY(convertInput(request)); const auto nnMeasure = NN_TRY(convertInput(measure)); const std::any resource = preparedModel->getUnderlyingResource(); if (const auto* model = std::any_cast(&resource)) { CHECK(*model != nullptr); NN_TRY(utils::makeGeneralFailure(nn::validateRequestForModel(nnRequest, **model), nn::ErrorStatus::INVALID_ARGUMENT)); } Task task = [preparedModel, nnRequest = std::move(nnRequest), nnMeasure, callback] { auto result = preparedModel->execute(nnRequest, nnMeasure, {}, {}); notify(callback.get(), std::move(result)); }; executor(std::move(task), userId, {}); return {}; } nn::GeneralResult execute_1_3(const nn::SharedPreparedModel& preparedModel, uid_t userId, const Executor& executor, const V1_3::Request& request, V1_2::MeasureTiming measure, const V1_3::OptionalTimePoint& deadline, const V1_3::OptionalTimeoutDuration& loopTimeoutDuration, const sp& callback) { if (callback.get() == nullptr) { return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT) << "Invalid callback"; } auto nnRequest = NN_TRY(convertInput(request)); const auto nnMeasure = NN_TRY(convertInput(measure)); const auto nnDeadline = NN_TRY(convertInput(deadline)); const auto nnLoopTimeoutDuration = NN_TRY(convertInput(loopTimeoutDuration)); const std::any resource = preparedModel->getUnderlyingResource(); if (const auto* model = std::any_cast(&resource)) { CHECK(*model != nullptr); NN_TRY(utils::makeGeneralFailure(nn::validateRequestForModel(nnRequest, **model), nn::ErrorStatus::INVALID_ARGUMENT)); } Task task = [preparedModel, nnRequest = std::move(nnRequest), nnMeasure, nnDeadline, nnLoopTimeoutDuration, callback] { auto result = preparedModel->execute(nnRequest, nnMeasure, nnDeadline, nnLoopTimeoutDuration); notify(callback.get(), std::move(result)); }; executor(std::move(task), userId, nnDeadline); return {}; } nn::ExecutionResult, V1_2::Timing>> executeSynchronously( const nn::SharedPreparedModel& preparedModel, const V1_0::Request& request, V1_2::MeasureTiming measure) { const auto nnRequest = NN_TRY(utils::makeExecutionFailure(convertInput(request))); const auto nnMeasure = NN_TRY(utils::makeExecutionFailure(convertInput(measure))); const auto [outputShapes, timing] = NN_TRY(preparedModel->execute(nnRequest, nnMeasure, {}, {})); auto hidlOutputShapes = NN_TRY(utils::makeExecutionFailure(V1_2::utils::convert(outputShapes))); const auto hidlTiming = NN_TRY(utils::makeExecutionFailure(V1_2::utils::convert(timing))); return std::make_pair(std::move(hidlOutputShapes), hidlTiming); } nn::ExecutionResult, V1_2::Timing>> executeSynchronously_1_3( const nn::SharedPreparedModel& preparedModel, const V1_3::Request& request, V1_2::MeasureTiming measure, const V1_3::OptionalTimePoint& deadline, const V1_3::OptionalTimeoutDuration& loopTimeoutDuration) { const auto nnRequest = NN_TRY(utils::makeExecutionFailure(convertInput(request))); const auto nnMeasure = NN_TRY(utils::makeExecutionFailure(convertInput(measure))); const auto nnDeadline = NN_TRY(utils::makeExecutionFailure(convertInput(deadline))); const auto nnLoopTimeoutDuration = NN_TRY(utils::makeExecutionFailure(convertInput(loopTimeoutDuration))); const auto [outputShapes, timing] = NN_TRY(preparedModel->execute(nnRequest, nnMeasure, nnDeadline, nnLoopTimeoutDuration)); auto hidlOutputShapes = NN_TRY(utils::makeExecutionFailure(V1_3::utils::convert(outputShapes))); const auto hidlTiming = NN_TRY(utils::makeExecutionFailure(V1_3::utils::convert(timing))); return std::make_pair(std::move(hidlOutputShapes), hidlTiming); } nn::GeneralResult> convertSyncFences( const hidl_vec& handles) { std::vector syncFences; syncFences.reserve(handles.size()); for (const auto& handle : handles) { auto nativeHandle = NN_TRY(convertInput(handle)); auto syncFence = NN_TRY(utils::makeGeneralFailure( nn::SyncFence::create(std::move(nativeHandle)), nn::ErrorStatus::INVALID_ARGUMENT)); syncFences.push_back(std::move(syncFence)); } return syncFences; } nn::GeneralResult>> executeFenced( const nn::SharedPreparedModel& preparedModel, const V1_3::Request& request, const hidl_vec& waitFor, V1_2::MeasureTiming measure, const V1_3::OptionalTimePoint& deadline, const V1_3::OptionalTimeoutDuration& loopTimeoutDuration, const V1_3::OptionalTimeoutDuration& duration) { const auto nnRequest = NN_TRY(convertInput(request)); const auto nnWaitFor = NN_TRY(convertSyncFences(waitFor)); const auto nnMeasure = NN_TRY(convertInput(measure)); const auto nnDeadline = NN_TRY(convertInput(deadline)); const auto nnLoopTimeoutDuration = NN_TRY(convertInput(loopTimeoutDuration)); const auto nnDuration = NN_TRY(convertInput(duration)); auto [syncFence, executeFencedCallback] = NN_TRY(preparedModel->executeFenced( nnRequest, nnWaitFor, nnMeasure, nnDeadline, nnLoopTimeoutDuration, nnDuration)); auto hidlSyncFence = NN_TRY(V1_3::utils::convert(syncFence.getSharedHandle())); auto hidlExecuteFencedCallback = sp::make(executeFencedCallback); return std::make_pair(std::move(hidlSyncFence), std::move(hidlExecuteFencedCallback)); } } // namespace PreparedModel::PreparedModel(nn::SharedPreparedModel preparedModel, Executor executor, uid_t userId) : kPreparedModel(std::move(preparedModel)), kExecutor(std::move(executor)), kUserId(userId) { CHECK(kPreparedModel != nullptr); CHECK(kExecutor != nullptr); } nn::SharedPreparedModel PreparedModel::getUnderlyingPreparedModel() const { return kPreparedModel; } Return PreparedModel::execute(const V1_0::Request& request, const sp& callback) { auto result = adapter::execute(kPreparedModel, kUserId, kExecutor, request, callback); if (!result.has_value()) { auto [message, code] = std::move(result).error(); LOG(ERROR) << "adapter::PreparedModel::execute failed with " << code << ": " << message; notify(callback.get(), code, {}, {}); return V1_0::utils::convert(code).value(); } return V1_0::ErrorStatus::NONE; } Return PreparedModel::execute_1_2(const V1_0::Request& request, V1_2::MeasureTiming measure, const sp& callback) { auto result = adapter::execute_1_2(kPreparedModel, kUserId, kExecutor, request, measure, callback); if (!result.has_value()) { auto [message, code] = std::move(result).error(); LOG(ERROR) << "adapter::PreparedModel::execute_1_2 failed with " << code << ": " << message; notify(callback.get(), code, {}, {}); return V1_2::utils::convert(code).value(); } return V1_0::ErrorStatus::NONE; } Return PreparedModel::execute_1_3( const V1_3::Request& request, V1_2::MeasureTiming measure, const V1_3::OptionalTimePoint& deadline, const V1_3::OptionalTimeoutDuration& loopTimeoutDuration, const sp& callback) { auto result = adapter::execute_1_3(kPreparedModel, kUserId, kExecutor, request, measure, deadline, loopTimeoutDuration, callback); if (!result.has_value()) { auto [message, code] = std::move(result).error(); LOG(ERROR) << "adapter::PreparedModel::execute_1_3 failed with " << code << ": " << message; notify(callback.get(), code, {}, {}); return V1_3::utils::convert(code).value(); } return V1_3::ErrorStatus::NONE; } Return PreparedModel::executeSynchronously(const V1_0::Request& request, V1_2::MeasureTiming measure, executeSynchronously_cb cb) { auto result = adapter::executeSynchronously(kPreparedModel, request, measure); if (!result.has_value()) { auto [message, code, outputShapes] = std::move(result).error(); LOG(ERROR) << "adapter::PreparedModel::executeSynchronously failed with " << code << ": " << message; cb(V1_2::utils::convert(code).value(), V1_2::utils::convert(outputShapes).value(), V1_2::utils::kNoTiming); return Void(); } auto [outputShapes, timing] = std::move(result).value(); cb(V1_0::ErrorStatus::NONE, outputShapes, timing); return Void(); } Return PreparedModel::executeSynchronously_1_3( const V1_3::Request& request, V1_2::MeasureTiming measure, const V1_3::OptionalTimePoint& deadline, const V1_3::OptionalTimeoutDuration& loopTimeoutDuration, executeSynchronously_1_3_cb cb) { auto result = adapter::executeSynchronously_1_3(kPreparedModel, request, measure, deadline, loopTimeoutDuration); if (!result.has_value()) { auto [message, code, outputShapes] = std::move(result).error(); LOG(ERROR) << "adapter::PreparedModel::executeSynchronously_1_3 failed with " << code << ": " << message; cb(V1_3::utils::convert(code).value(), V1_3::utils::convert(outputShapes).value(), V1_2::utils::kNoTiming); return Void(); } auto [outputShapes, timing] = std::move(result).value(); cb(V1_3::ErrorStatus::NONE, outputShapes, timing); return Void(); } Return PreparedModel::configureExecutionBurst( const sp& callback, const MQDescriptorSync& requestChannel, const MQDescriptorSync& resultChannel, configureExecutionBurst_cb cb) { const sp burst = nn::ExecutionBurstServer::create( callback, requestChannel, resultChannel, this, std::chrono::microseconds{0}); if (burst == nullptr) { cb(V1_0::ErrorStatus::GENERAL_FAILURE, {}); } else { cb(V1_0::ErrorStatus::NONE, burst); } return Void(); } Return PreparedModel::executeFenced(const V1_3::Request& request, const hidl_vec& waitFor, V1_2::MeasureTiming measure, const V1_3::OptionalTimePoint& deadline, const V1_3::OptionalTimeoutDuration& loopTimeoutDuration, const V1_3::OptionalTimeoutDuration& duration, executeFenced_cb callback) { auto result = adapter::executeFenced(kPreparedModel, request, waitFor, measure, deadline, loopTimeoutDuration, duration); if (!result.has_value()) { auto [message, code] = std::move(result).error(); LOG(ERROR) << "adapter::PreparedModel::executeFenced failed with " << code << ": " << message; callback(V1_3::utils::convert(code).value(), {}, nullptr); return Void(); } auto [syncFence, executeFencedCallback] = std::move(result).value(); callback(V1_3::ErrorStatus::NONE, syncFence, executeFencedCallback); return Void(); } } // namespace android::hardware::neuralnetworks::adapter