blob: 2781053d07844add5eb1462a8ab582e0eacbfa97 [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>
30#include <nnapi/Types.h>
31#include <nnapi/hal/1.2/Conversions.h>
32#include <nnapi/hal/CommonUtils.h>
33#include <nnapi/hal/HandleError.h>
34#include <nnapi/hal/ProtectCallback.h>
35
36#include <memory>
37#include <tuple>
38#include <utility>
39#include <vector>
40
41namespace android::hardware::neuralnetworks::V1_3::utils {
42namespace {
43
44nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
45convertExecutionResultsHelper(const hidl_vec<V1_2::OutputShape>& outputShapes,
46 const V1_2::Timing& timing) {
47 return std::make_pair(NN_TRY(validatedConvertToCanonical(outputShapes)),
48 NN_TRY(validatedConvertToCanonical(timing)));
49}
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) {
58 return std::make_pair(NN_TRY(validatedConvertToCanonical(timingLaunched)),
59 NN_TRY(validatedConvertToCanonical(timingFenced)));
60}
61
62nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
63convertExecuteFencedResults(const hidl_handle& syncFence,
64 const sp<IFencedExecutionCallback>& callback) {
65 auto resultSyncFence = nn::SyncFence::createAsSignaled();
66 if (syncFence.getNativeHandle() != nullptr) {
67 auto nativeHandle = NN_TRY(validatedConvertToCanonical(syncFence));
68 resultSyncFence = NN_TRY(hal::utils::makeGeneralFailure(
69 nn::SyncFence::create(std::move(nativeHandle)), nn::ErrorStatus::GENERAL_FAILURE));
70 }
71
72 if (callback == nullptr) {
73 return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "callback is null";
74 }
75
76 // Create callback which can be used to retrieve the execution error status and timings.
77 nn::ExecuteFencedInfoCallback resultCallback =
78 [callback]() -> nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> {
79 nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> result =
80 NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
81 auto cb = [&result](ErrorStatus status, const V1_2::Timing& timingLaunched,
82 const V1_2::Timing& timingFenced) {
83 if (status != ErrorStatus::NONE) {
84 const auto canonical = validatedConvertToCanonical(status).value_or(
85 nn::ErrorStatus::GENERAL_FAILURE);
86 result = NN_ERROR(canonical) << "getExecutionInfo failed with " << toString(status);
87 } else {
88 result = convertFencedExecutionCallbackResults(timingLaunched, timingFenced);
89 }
90 };
91
92 const auto ret = callback->getExecutionInfo(cb);
93 NN_TRY(hal::utils::handleTransportError(ret));
94
95 return result;
96 };
97
98 return std::make_pair(std::move(resultSyncFence), std::move(resultCallback));
99}
100
101} // namespace
102
103nn::GeneralResult<std::shared_ptr<const PreparedModel>> PreparedModel::create(
104 sp<V1_3::IPreparedModel> preparedModel) {
105 if (preparedModel == nullptr) {
106 return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
107 << "V1_3::utils::PreparedModel::create must have non-null preparedModel";
108 }
109
110 auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(preparedModel));
111 return std::make_shared<const PreparedModel>(PrivateConstructorTag{}, std::move(preparedModel),
112 std::move(deathHandler));
113}
114
115PreparedModel::PreparedModel(PrivateConstructorTag /*tag*/, sp<V1_3::IPreparedModel> preparedModel,
116 hal::utils::DeathHandler deathHandler)
117 : kPreparedModel(std::move(preparedModel)), kDeathHandler(std::move(deathHandler)) {}
118
119nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
120PreparedModel::executeSynchronously(const Request& request, V1_2::MeasureTiming measure,
121 const OptionalTimePoint& deadline,
122 const OptionalTimeoutDuration& loopTimeoutDuration) const {
123 nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> result =
124 NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
125 const auto cb = [&result](ErrorStatus status, const hidl_vec<V1_2::OutputShape>& outputShapes,
126 const V1_2::Timing& timing) {
127 if (status != ErrorStatus::NONE) {
128 const auto canonical =
129 validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
130 result = NN_ERROR(canonical) << "executeSynchronously failed with " << toString(status);
131 } else {
132 result = convertExecutionResults(outputShapes, timing);
133 }
134 };
135
136 const auto ret = kPreparedModel->executeSynchronously_1_3(request, measure, deadline,
137 loopTimeoutDuration, cb);
138 NN_TRY(hal::utils::makeExecutionFailure(hal::utils::handleTransportError(ret)));
139
140 return result;
141}
142
143nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
144PreparedModel::executeAsynchronously(const Request& request, V1_2::MeasureTiming measure,
145 const OptionalTimePoint& deadline,
146 const OptionalTimeoutDuration& loopTimeoutDuration) const {
147 const auto cb = sp<ExecutionCallback>::make();
148 const auto scoped = kDeathHandler.protectCallback(cb.get());
149
150 const auto ret =
151 kPreparedModel->execute_1_3(request, measure, deadline, loopTimeoutDuration, cb);
152 const auto status =
153 NN_TRY(hal::utils::makeExecutionFailure(hal::utils::handleTransportError(ret)));
154 if (status != ErrorStatus::NONE) {
155 const auto canonical =
156 validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
157 return NN_ERROR(canonical) << "executeAsynchronously failed with " << toString(status);
158 }
159
160 return cb->get();
161}
162
163nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> PreparedModel::execute(
164 const nn::Request& request, nn::MeasureTiming measure,
165 const nn::OptionalTimePoint& deadline,
166 const nn::OptionalTimeoutDuration& loopTimeoutDuration) const {
167 // Ensure that request is ready for IPC.
168 std::optional<nn::Request> maybeRequestInShared;
169 const nn::Request& requestInShared = NN_TRY(hal::utils::makeExecutionFailure(
170 hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared)));
171
172 const auto hidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
173 const auto hidlMeasure =
174 NN_TRY(hal::utils::makeExecutionFailure(V1_2::utils::convert(measure)));
175 const auto hidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
176 const auto hidlLoopTimeoutDuration =
177 NN_TRY(hal::utils::makeExecutionFailure(convert(loopTimeoutDuration)));
178
179 nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> result =
180 NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
181 const bool preferSynchronous = true;
182
183 // Execute synchronously if allowed.
184 if (preferSynchronous) {
185 result = executeSynchronously(hidlRequest, hidlMeasure, hidlDeadline,
186 hidlLoopTimeoutDuration);
187 }
188
189 // Run asymchronous execution if execution has not already completed.
190 if (!result.has_value()) {
191 result = executeAsynchronously(hidlRequest, hidlMeasure, hidlDeadline,
192 hidlLoopTimeoutDuration);
193 }
194
195 // Flush output buffers if suxcessful execution.
196 if (result.has_value()) {
197 NN_TRY(hal::utils::makeExecutionFailure(
198 hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
199 }
200
201 return result;
202}
203
204nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
205PreparedModel::executeFenced(const nn::Request& request, const std::vector<nn::SyncFence>& waitFor,
206 nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline,
207 const nn::OptionalTimeoutDuration& loopTimeoutDuration,
208 const nn::OptionalTimeoutDuration& timeoutDurationAfterFence) const {
209 // Ensure that request is ready for IPC.
210 std::optional<nn::Request> maybeRequestInShared;
211 const nn::Request& requestInShared =
212 NN_TRY(hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared));
213
214 const auto hidlRequest = NN_TRY(convert(requestInShared));
Slava Shklyaev49817a02020-10-27 18:44:01 +0000215 const auto hidlWaitFor = NN_TRY(hal::utils::convertSyncFences(waitFor));
Michael Butler4b276a72020-08-06 23:22:35 -0700216 const auto hidlMeasure = NN_TRY(V1_2::utils::convert(measure));
217 const auto hidlDeadline = NN_TRY(convert(deadline));
218 const auto hidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
219 const auto hidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence));
220
221 nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> result =
222 NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
223 auto cb = [&result](ErrorStatus status, const hidl_handle& syncFence,
224 const sp<IFencedExecutionCallback>& callback) {
225 if (status != ErrorStatus::NONE) {
226 const auto canonical =
227 validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
228 result = NN_ERROR(canonical) << "executeFenced failed with " << toString(status);
229 } else {
230 result = convertExecuteFencedResults(syncFence, callback);
231 }
232 };
233
234 const auto ret = kPreparedModel->executeFenced(hidlRequest, hidlWaitFor, hidlMeasure,
235 hidlDeadline, hidlLoopTimeoutDuration,
236 hidlTimeoutDurationAfterFence, cb);
237 NN_TRY(hal::utils::handleTransportError(ret));
238 auto [syncFence, callback] = NN_TRY(std::move(result));
239
240 // If executeFenced required the request memory to be moved into shared memory, block here until
241 // the fenced execution has completed and flush the memory back.
242 if (maybeRequestInShared.has_value()) {
243 const auto state = syncFence.syncWait({});
244 if (state != nn::SyncFence::FenceState::SIGNALED) {
245 return NN_ERROR() << "syncWait failed with " << state;
246 }
247 NN_TRY(hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared));
248 }
249
250 return std::make_pair(std::move(syncFence), std::move(callback));
251}
252
253std::any PreparedModel::getUnderlyingResource() const {
254 sp<V1_3::IPreparedModel> resource = kPreparedModel;
255 return resource;
256}
257
258} // namespace android::hardware::neuralnetworks::V1_3::utils