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