blob: 5d821108290139738ffbff2757c9271211a37b70 [file] [log] [blame]
Michael Butler4b276a72020-08-06 23:22:35 -07001/*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "PreparedModel.h"
18
19#include "Callbacks.h"
20#include "Conversions.h"
21#include "Utils.h"
22
23#include <android/hardware/neuralnetworks/1.0/types.h>
24#include <android/hardware/neuralnetworks/1.1/types.h>
25#include <android/hardware/neuralnetworks/1.2/types.h>
26#include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
27#include <android/hardware/neuralnetworks/1.3/types.h>
28#include <nnapi/IPreparedModel.h>
29#include <nnapi/Result.h>
Michael Butler6547b2a2020-11-22 19:36:30 -080030#include <nnapi/TypeUtils.h>
Michael Butler4b276a72020-08-06 23:22:35 -070031#include <nnapi/Types.h>
32#include <nnapi/hal/1.2/Conversions.h>
33#include <nnapi/hal/CommonUtils.h>
34#include <nnapi/hal/HandleError.h>
35#include <nnapi/hal/ProtectCallback.h>
36
37#include <memory>
38#include <tuple>
39#include <utility>
40#include <vector>
41
42namespace android::hardware::neuralnetworks::V1_3::utils {
43namespace {
44
45nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
46convertExecutionResultsHelper(const hidl_vec<V1_2::OutputShape>& outputShapes,
47 const V1_2::Timing& timing) {
Michael Butler6547b2a2020-11-22 19:36:30 -080048 return std::make_pair(NN_TRY(nn::convert(outputShapes)), NN_TRY(nn::convert(timing)));
Michael Butler4b276a72020-08-06 23:22:35 -070049}
50
51nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> convertExecutionResults(
52 const hidl_vec<V1_2::OutputShape>& outputShapes, const V1_2::Timing& timing) {
53 return hal::utils::makeExecutionFailure(convertExecutionResultsHelper(outputShapes, timing));
54}
55
Michael Butler4b276a72020-08-06 23:22:35 -070056nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> convertFencedExecutionCallbackResults(
57 const V1_2::Timing& timingLaunched, const V1_2::Timing& timingFenced) {
Michael Butler6547b2a2020-11-22 19:36:30 -080058 return std::make_pair(NN_TRY(nn::convert(timingLaunched)), NN_TRY(nn::convert(timingFenced)));
Michael Butler4b276a72020-08-06 23:22:35 -070059}
60
61nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
62convertExecuteFencedResults(const hidl_handle& syncFence,
63 const sp<IFencedExecutionCallback>& callback) {
64 auto resultSyncFence = nn::SyncFence::createAsSignaled();
65 if (syncFence.getNativeHandle() != nullptr) {
Michael Butler6547b2a2020-11-22 19:36:30 -080066 auto sharedHandle = NN_TRY(nn::convert(syncFence));
Michael Butler4b276a72020-08-06 23:22:35 -070067 resultSyncFence = NN_TRY(hal::utils::makeGeneralFailure(
Michael Butler6547b2a2020-11-22 19:36:30 -080068 nn::SyncFence::create(std::move(sharedHandle)), nn::ErrorStatus::GENERAL_FAILURE));
Michael Butler4b276a72020-08-06 23:22:35 -070069 }
70
71 if (callback == nullptr) {
72 return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "callback is null";
73 }
74
75 // Create callback which can be used to retrieve the execution error status and timings.
76 nn::ExecuteFencedInfoCallback resultCallback =
77 [callback]() -> nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> {
78 nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> result =
79 NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
80 auto cb = [&result](ErrorStatus status, const V1_2::Timing& timingLaunched,
81 const V1_2::Timing& timingFenced) {
82 if (status != ErrorStatus::NONE) {
Michael Butler6547b2a2020-11-22 19:36:30 -080083 const auto canonical =
84 nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
Michael Butler4b276a72020-08-06 23:22:35 -070085 result = NN_ERROR(canonical) << "getExecutionInfo failed with " << toString(status);
86 } else {
87 result = convertFencedExecutionCallbackResults(timingLaunched, timingFenced);
88 }
89 };
90
91 const auto ret = callback->getExecutionInfo(cb);
92 NN_TRY(hal::utils::handleTransportError(ret));
93
94 return result;
95 };
96
97 return std::make_pair(std::move(resultSyncFence), std::move(resultCallback));
98}
99
100} // namespace
101
102nn::GeneralResult<std::shared_ptr<const PreparedModel>> PreparedModel::create(
103 sp<V1_3::IPreparedModel> preparedModel) {
104 if (preparedModel == nullptr) {
105 return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
106 << "V1_3::utils::PreparedModel::create must have non-null preparedModel";
107 }
108
109 auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(preparedModel));
110 return std::make_shared<const PreparedModel>(PrivateConstructorTag{}, std::move(preparedModel),
111 std::move(deathHandler));
112}
113
114PreparedModel::PreparedModel(PrivateConstructorTag /*tag*/, sp<V1_3::IPreparedModel> preparedModel,
115 hal::utils::DeathHandler deathHandler)
116 : kPreparedModel(std::move(preparedModel)), kDeathHandler(std::move(deathHandler)) {}
117
118nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
119PreparedModel::executeSynchronously(const Request& request, V1_2::MeasureTiming measure,
120 const OptionalTimePoint& deadline,
121 const OptionalTimeoutDuration& loopTimeoutDuration) const {
122 nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> result =
123 NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
124 const auto cb = [&result](ErrorStatus status, const hidl_vec<V1_2::OutputShape>& outputShapes,
125 const V1_2::Timing& timing) {
126 if (status != ErrorStatus::NONE) {
Michael Butler6547b2a2020-11-22 19:36:30 -0800127 const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
Michael Butler4b276a72020-08-06 23:22:35 -0700128 result = NN_ERROR(canonical) << "executeSynchronously failed with " << toString(status);
129 } else {
130 result = convertExecutionResults(outputShapes, timing);
131 }
132 };
133
134 const auto ret = kPreparedModel->executeSynchronously_1_3(request, measure, deadline,
135 loopTimeoutDuration, cb);
136 NN_TRY(hal::utils::makeExecutionFailure(hal::utils::handleTransportError(ret)));
137
138 return result;
139}
140
141nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
142PreparedModel::executeAsynchronously(const Request& request, V1_2::MeasureTiming measure,
143 const OptionalTimePoint& deadline,
144 const OptionalTimeoutDuration& loopTimeoutDuration) const {
145 const auto cb = sp<ExecutionCallback>::make();
146 const auto scoped = kDeathHandler.protectCallback(cb.get());
147
148 const auto ret =
149 kPreparedModel->execute_1_3(request, measure, deadline, loopTimeoutDuration, cb);
150 const auto status =
151 NN_TRY(hal::utils::makeExecutionFailure(hal::utils::handleTransportError(ret)));
152 if (status != ErrorStatus::NONE) {
Michael Butler6547b2a2020-11-22 19:36:30 -0800153 const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
Michael Butler4b276a72020-08-06 23:22:35 -0700154 return NN_ERROR(canonical) << "executeAsynchronously failed with " << toString(status);
155 }
156
157 return cb->get();
158}
159
160nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> PreparedModel::execute(
161 const nn::Request& request, nn::MeasureTiming measure,
162 const nn::OptionalTimePoint& deadline,
163 const nn::OptionalTimeoutDuration& loopTimeoutDuration) const {
164 // Ensure that request is ready for IPC.
165 std::optional<nn::Request> maybeRequestInShared;
166 const nn::Request& requestInShared = NN_TRY(hal::utils::makeExecutionFailure(
167 hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared)));
168
169 const auto hidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
170 const auto hidlMeasure =
171 NN_TRY(hal::utils::makeExecutionFailure(V1_2::utils::convert(measure)));
172 const auto hidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
173 const auto hidlLoopTimeoutDuration =
174 NN_TRY(hal::utils::makeExecutionFailure(convert(loopTimeoutDuration)));
175
176 nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> result =
177 NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
178 const bool preferSynchronous = true;
179
180 // Execute synchronously if allowed.
181 if (preferSynchronous) {
182 result = executeSynchronously(hidlRequest, hidlMeasure, hidlDeadline,
183 hidlLoopTimeoutDuration);
184 }
185
186 // Run asymchronous execution if execution has not already completed.
187 if (!result.has_value()) {
188 result = executeAsynchronously(hidlRequest, hidlMeasure, hidlDeadline,
189 hidlLoopTimeoutDuration);
190 }
191
192 // Flush output buffers if suxcessful execution.
193 if (result.has_value()) {
194 NN_TRY(hal::utils::makeExecutionFailure(
195 hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
196 }
197
198 return result;
199}
200
201nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
202PreparedModel::executeFenced(const nn::Request& request, const std::vector<nn::SyncFence>& waitFor,
203 nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline,
204 const nn::OptionalTimeoutDuration& loopTimeoutDuration,
205 const nn::OptionalTimeoutDuration& timeoutDurationAfterFence) const {
206 // Ensure that request is ready for IPC.
207 std::optional<nn::Request> maybeRequestInShared;
208 const nn::Request& requestInShared =
209 NN_TRY(hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared));
210
211 const auto hidlRequest = NN_TRY(convert(requestInShared));
Slava Shklyaev49817a02020-10-27 18:44:01 +0000212 const auto hidlWaitFor = NN_TRY(hal::utils::convertSyncFences(waitFor));
Michael Butler4b276a72020-08-06 23:22:35 -0700213 const auto hidlMeasure = NN_TRY(V1_2::utils::convert(measure));
214 const auto hidlDeadline = NN_TRY(convert(deadline));
215 const auto hidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
216 const auto hidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence));
217
218 nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> result =
219 NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
220 auto cb = [&result](ErrorStatus status, const hidl_handle& syncFence,
221 const sp<IFencedExecutionCallback>& callback) {
222 if (status != ErrorStatus::NONE) {
Michael Butler6547b2a2020-11-22 19:36:30 -0800223 const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
Michael Butler4b276a72020-08-06 23:22:35 -0700224 result = NN_ERROR(canonical) << "executeFenced failed with " << toString(status);
225 } else {
226 result = convertExecuteFencedResults(syncFence, callback);
227 }
228 };
229
230 const auto ret = kPreparedModel->executeFenced(hidlRequest, hidlWaitFor, hidlMeasure,
231 hidlDeadline, hidlLoopTimeoutDuration,
232 hidlTimeoutDurationAfterFence, cb);
233 NN_TRY(hal::utils::handleTransportError(ret));
234 auto [syncFence, callback] = NN_TRY(std::move(result));
235
236 // If executeFenced required the request memory to be moved into shared memory, block here until
237 // the fenced execution has completed and flush the memory back.
238 if (maybeRequestInShared.has_value()) {
239 const auto state = syncFence.syncWait({});
240 if (state != nn::SyncFence::FenceState::SIGNALED) {
241 return NN_ERROR() << "syncWait failed with " << state;
242 }
243 NN_TRY(hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared));
244 }
245
246 return std::make_pair(std::move(syncFence), std::move(callback));
247}
248
249std::any PreparedModel::getUnderlyingResource() const {
250 sp<V1_3::IPreparedModel> resource = kPreparedModel;
251 return resource;
252}
253
254} // namespace android::hardware::neuralnetworks::V1_3::utils