blob: e07eb4750e6522fae4274d1f8810852cac5939aa [file] [log] [blame]
Michael Butler616701d2020-01-07 14:52:44 -08001/*
2 * Copyright (C) 2019 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 "1.0/Utils.h"
18#include "1.3/Callbacks.h"
19#include "1.3/Utils.h"
20#include "GeneratedTestHarness.h"
21#include "Utils.h"
22
23namespace android::hardware::neuralnetworks::V1_3::vts::functional {
24
25using implementation::ExecutionCallback;
26using implementation::PreparedModelCallback;
27using test_helper::TestBuffer;
28using test_helper::TestModel;
29using V1_1::ExecutionPreference;
30using V1_2::MeasureTiming;
31using V1_2::OutputShape;
32using V1_2::Timing;
33
34using HidlToken =
35 hidl_array<uint8_t, static_cast<uint32_t>(V1_2::Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
36
37enum class DeadlineBoundType { NOW, UNLIMITED };
38constexpr std::array<DeadlineBoundType, 2> deadlineBounds = {DeadlineBoundType::NOW,
39 DeadlineBoundType::UNLIMITED};
40std::string toString(DeadlineBoundType type) {
41 switch (type) {
42 case DeadlineBoundType::NOW:
43 return "NOW";
44 case DeadlineBoundType::UNLIMITED:
45 return "UNLIMITED";
46 }
47 LOG(FATAL) << "Unrecognized DeadlineBoundType: " << static_cast<int>(type);
48 return {};
49}
50
51using Results = std::tuple<ErrorStatus, hidl_vec<OutputShape>, Timing>;
52using MaybeResults = std::optional<Results>;
53
54using ExecutionFunction =
55 std::function<MaybeResults(const sp<IPreparedModel>& preparedModel, const Request& request,
56 DeadlineBoundType deadlineBound)>;
57
58static OptionalTimePoint makeOptionalTimePoint(DeadlineBoundType deadlineBoundType) {
59 OptionalTimePoint deadline;
60 switch (deadlineBoundType) {
61 case DeadlineBoundType::NOW: {
62 const auto currentTime = std::chrono::steady_clock::now();
63 const auto currentTimeInNanoseconds =
64 std::chrono::time_point_cast<std::chrono::nanoseconds>(currentTime);
65 const uint64_t nanosecondsSinceEpoch =
66 currentTimeInNanoseconds.time_since_epoch().count();
Michael Butler6a4172c2020-02-04 16:15:04 -080067 deadline.nanosecondsSinceEpoch(nanosecondsSinceEpoch);
Michael Butler616701d2020-01-07 14:52:44 -080068 } break;
69 case DeadlineBoundType::UNLIMITED: {
70 uint64_t unlimited = std::numeric_limits<uint64_t>::max();
Michael Butler6a4172c2020-02-04 16:15:04 -080071 deadline.nanosecondsSinceEpoch(unlimited);
Michael Butler616701d2020-01-07 14:52:44 -080072 } break;
73 }
74 return deadline;
75}
76
77void runPrepareModelTest(const sp<IDevice>& device, const Model& model, Priority priority,
78 std::optional<DeadlineBoundType> deadlineBound) {
79 OptionalTimePoint deadline;
80 if (deadlineBound.has_value()) {
81 deadline = makeOptionalTimePoint(deadlineBound.value());
82 }
83
84 // see if service can handle model
85 bool fullySupportsModel = false;
86 const Return<void> supportedCall = device->getSupportedOperations_1_3(
87 model, [&fullySupportsModel](ErrorStatus status, const hidl_vec<bool>& supported) {
88 ASSERT_EQ(ErrorStatus::NONE, status);
89 ASSERT_NE(0ul, supported.size());
90 fullySupportsModel = std::all_of(supported.begin(), supported.end(),
91 [](bool valid) { return valid; });
92 });
93 ASSERT_TRUE(supportedCall.isOk());
94
95 // launch prepare model
96 const sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
97 const Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_3(
98 model, ExecutionPreference::FAST_SINGLE_ANSWER, priority, deadline,
99 hidl_vec<hidl_handle>(), hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
100 ASSERT_TRUE(prepareLaunchStatus.isOk());
101 ASSERT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(prepareLaunchStatus));
102
103 // retrieve prepared model
104 preparedModelCallback->wait();
105 const ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
106 const sp<V1_0::IPreparedModel> preparedModelV1_0 = preparedModelCallback->getPreparedModel();
107 const sp<IPreparedModel> preparedModel =
108 IPreparedModel::castFrom(preparedModelV1_0).withDefault(nullptr);
109
110 // The getSupportedOperations_1_3 call returns a list of operations that are
111 // guaranteed not to fail if prepareModel_1_3 is called, and
112 // 'fullySupportsModel' is true i.f.f. the entire model is guaranteed.
113 // If a driver has any doubt that it can prepare an operation, it must
114 // return false. So here, if a driver isn't sure if it can support an
115 // operation, but reports that it successfully prepared the model, the test
116 // can continue.
117 if (!fullySupportsModel && prepareReturnStatus != ErrorStatus::NONE) {
118 ASSERT_EQ(nullptr, preparedModel.get());
119 return;
120 }
121
122 // verify return status
123 if (!deadlineBound.has_value()) {
124 EXPECT_EQ(ErrorStatus::NONE, prepareReturnStatus);
125 } else {
126 switch (deadlineBound.value()) {
127 case DeadlineBoundType::NOW:
128 // If the execution was launched with a deadline of NOW, the
129 // deadline has already passed when the driver would launch the
130 // execution. In this case, the driver must return
131 // MISSED_DEADLINE_*.
132 EXPECT_TRUE(prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
133 prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_PERSISTENT);
134 break;
135 case DeadlineBoundType::UNLIMITED:
136 // If an unlimited deadline is supplied, we expect the execution to
137 // proceed normally. In this case, check it normally by breaking out
138 // of the switch statement.
139 EXPECT_EQ(ErrorStatus::NONE, prepareReturnStatus);
140 break;
141 }
142 }
143 ASSERT_EQ(prepareReturnStatus == ErrorStatus::NONE, preparedModel.get() != nullptr);
144}
145
146void runPrepareModelTests(const sp<IDevice>& device, const Model& model,
147 bool supportsPrepareModelDeadline) {
148 // test priority
149 for (auto priority : hidl_enum_range<Priority>{}) {
150 SCOPED_TRACE("priority: " + toString(priority));
151 if (priority == kDefaultPriority) continue;
152 runPrepareModelTest(device, model, priority, {});
153 }
154
155 // test deadline
156 if (supportsPrepareModelDeadline) {
157 for (auto deadlineBound : deadlineBounds) {
158 SCOPED_TRACE("deadlineBound: " + toString(deadlineBound));
159 runPrepareModelTest(device, model, kDefaultPriority, deadlineBound);
160 }
161 }
162}
163
164static MaybeResults executeAsynchronously(const sp<IPreparedModel>& preparedModel,
165 const Request& request, DeadlineBoundType deadlineBound) {
166 SCOPED_TRACE("asynchronous");
167 const MeasureTiming measure = MeasureTiming::NO;
168 const OptionalTimePoint deadline = makeOptionalTimePoint(deadlineBound);
169
170 // launch execution
171 const sp<ExecutionCallback> callback = new ExecutionCallback();
172 Return<ErrorStatus> ret = preparedModel->execute_1_3(request, measure, deadline, callback);
173 EXPECT_TRUE(ret.isOk());
174 EXPECT_EQ(ErrorStatus::NONE, ret.withDefault(ErrorStatus::GENERAL_FAILURE));
175 if (!ret.isOk() || ret != ErrorStatus::NONE) return std::nullopt;
176
177 // retrieve execution results
178 callback->wait();
179 const ErrorStatus status = callback->getStatus();
180 hidl_vec<OutputShape> outputShapes = callback->getOutputShapes();
181 const Timing timing = callback->getTiming();
182
183 // return results
184 return Results{status, std::move(outputShapes), timing};
185}
186
187static MaybeResults executeSynchronously(const sp<IPreparedModel>& preparedModel,
188 const Request& request, DeadlineBoundType deadlineBound) {
189 SCOPED_TRACE("synchronous");
190 const MeasureTiming measure = MeasureTiming::NO;
191 const OptionalTimePoint deadline = makeOptionalTimePoint(deadlineBound);
192
193 // configure results callback
194 MaybeResults results;
195 const auto cb = [&results](const auto&... args) { *results = {args...}; };
196
197 // run execution
198 const Return<void> ret =
199 preparedModel->executeSynchronously_1_3(request, measure, deadline, cb);
200 EXPECT_TRUE(ret.isOk());
201 if (!ret.isOk()) return std::nullopt;
202
203 // return results
204 return results;
205}
206
207void runExecutionTest(const sp<IPreparedModel>& preparedModel, const TestModel& testModel,
208 const Request& request, bool synchronous, DeadlineBoundType deadlineBound) {
209 const ExecutionFunction execute = synchronous ? executeSynchronously : executeAsynchronously;
210
211 // Perform execution and unpack results.
212 const auto results = execute(preparedModel, request, deadlineBound);
213 if (!results.has_value()) return;
214 const auto& [status, outputShapes, timing] = results.value();
215
216 // Verify no timing information was returned
217 EXPECT_EQ(UINT64_MAX, timing.timeOnDevice);
218 EXPECT_EQ(UINT64_MAX, timing.timeInDriver);
219
220 // Validate deadline information if applicable.
221 switch (deadlineBound) {
222 case DeadlineBoundType::NOW:
223 // If the execution was launched with a deadline of NOW, the
224 // deadline has already passed when the driver would launch the
225 // execution. In this case, the driver must return
226 // MISSED_DEADLINE_*.
227 ASSERT_TRUE(status == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
228 status == ErrorStatus::MISSED_DEADLINE_PERSISTENT);
229 return;
230 case DeadlineBoundType::UNLIMITED:
231 // If an unlimited deadline is supplied, we expect the execution to
232 // proceed normally. In this case, check it normally by breaking out
233 // of the switch statement.
234 ASSERT_EQ(ErrorStatus::NONE, status);
235 break;
236 }
237
238 // If the model output operands are fully specified, outputShapes must be either
239 // either empty, or have the same number of elements as the number of outputs.
Slava Shklyaev1f98e2e2020-01-31 15:14:24 +0000240 ASSERT_TRUE(outputShapes.size() == 0 ||
241 outputShapes.size() == testModel.main.outputIndexes.size());
Michael Butler616701d2020-01-07 14:52:44 -0800242
243 // Go through all outputs, check returned output shapes.
244 for (uint32_t i = 0; i < outputShapes.size(); i++) {
245 EXPECT_TRUE(outputShapes[i].isSufficient);
Slava Shklyaev1f98e2e2020-01-31 15:14:24 +0000246 const auto& expect = testModel.main.operands[testModel.main.outputIndexes[i]].dimensions;
Michael Butler616701d2020-01-07 14:52:44 -0800247 const std::vector<uint32_t> actual = outputShapes[i].dimensions;
248 EXPECT_EQ(expect, actual);
249 }
250
251 // Retrieve execution results.
252 ASSERT_TRUE(nn::compliantWithV1_0(request));
253 const V1_0::Request request10 = nn::convertToV1_0(request);
254 const std::vector<TestBuffer> outputs = getOutputBuffers(request10);
255
256 // We want "close-enough" results.
257 checkResults(testModel, outputs);
258}
259
260void runExecutionTests(const sp<IPreparedModel>& preparedModel, const TestModel& testModel,
261 const Request& request) {
262 for (bool synchronous : {false, true}) {
263 for (auto deadlineBound : deadlineBounds) {
264 runExecutionTest(preparedModel, testModel, request, synchronous, deadlineBound);
265 }
266 }
267}
268
269void runTests(const sp<IDevice>& device, const TestModel& testModel,
270 std::pair<bool, bool> supportsDeadlines) {
271 // setup
272 const auto [supportsPrepareModelDeadline, supportsExecutionDeadline] = supportsDeadlines;
273 if (!supportsPrepareModelDeadline && !supportsExecutionDeadline) return;
274 const Model model = createModel(testModel);
275
276 // run prepare model tests
277 runPrepareModelTests(device, model, supportsPrepareModelDeadline);
278
279 if (supportsExecutionDeadline) {
280 // prepare model
281 sp<IPreparedModel> preparedModel;
282 createPreparedModel(device, model, &preparedModel);
283 if (preparedModel == nullptr) return;
284
285 // run execution tests
286 const Request request = nn::convertToV1_3(createRequest(testModel));
287 runExecutionTests(preparedModel, testModel, request);
288 }
289}
290
291class DeadlineTest : public GeneratedTestBase {};
292
293TEST_P(DeadlineTest, Test) {
294 runTests(kDevice, kTestModel, mSupportsDeadlines);
295}
296
297INSTANTIATE_GENERATED_TEST(DeadlineTest,
298 [](const TestModel& testModel) { return !testModel.expectFailure; });
299
300} // namespace android::hardware::neuralnetworks::V1_3::vts::functional