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