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: |
Michael Butler | c7327c5 | 2020-02-18 18:38:37 -0800 | [diff] [blame] | 137 | case DeadlineBoundType::SHORT: |
| 138 | // Either the driver successfully completed the task or it |
| 139 | // aborted and returned MISSED_DEADLINE_*. |
Michael Butler | 9b9a804 | 2020-02-13 16:37:22 -0800 | [diff] [blame] | 140 | EXPECT_TRUE(prepareReturnStatus == ErrorStatus::NONE || |
| 141 | prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_TRANSIENT || |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 142 | prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_PERSISTENT); |
| 143 | break; |
| 144 | case DeadlineBoundType::UNLIMITED: |
| 145 | // If an unlimited deadline is supplied, we expect the execution to |
| 146 | // proceed normally. In this case, check it normally by breaking out |
| 147 | // of the switch statement. |
| 148 | EXPECT_EQ(ErrorStatus::NONE, prepareReturnStatus); |
| 149 | break; |
| 150 | } |
| 151 | } |
| 152 | ASSERT_EQ(prepareReturnStatus == ErrorStatus::NONE, preparedModel.get() != nullptr); |
| 153 | } |
| 154 | |
Michael Butler | 9b9a804 | 2020-02-13 16:37:22 -0800 | [diff] [blame] | 155 | void runPrepareModelTests(const sp<IDevice>& device, const Model& model) { |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 156 | // test priority |
| 157 | for (auto priority : hidl_enum_range<Priority>{}) { |
| 158 | SCOPED_TRACE("priority: " + toString(priority)); |
| 159 | if (priority == kDefaultPriority) continue; |
| 160 | runPrepareModelTest(device, model, priority, {}); |
| 161 | } |
| 162 | |
| 163 | // test deadline |
Michael Butler | 9b9a804 | 2020-02-13 16:37:22 -0800 | [diff] [blame] | 164 | for (auto deadlineBound : deadlineBounds) { |
| 165 | SCOPED_TRACE("deadlineBound: " + toString(deadlineBound)); |
| 166 | runPrepareModelTest(device, model, kDefaultPriority, deadlineBound); |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 167 | } |
| 168 | } |
| 169 | |
| 170 | static MaybeResults executeAsynchronously(const sp<IPreparedModel>& preparedModel, |
Michael Butler | 9b9a804 | 2020-02-13 16:37:22 -0800 | [diff] [blame] | 171 | const Request& request, |
| 172 | const OptionalTimePoint& deadline) { |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 173 | SCOPED_TRACE("asynchronous"); |
| 174 | const MeasureTiming measure = MeasureTiming::NO; |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 175 | |
| 176 | // launch execution |
| 177 | const sp<ExecutionCallback> callback = new ExecutionCallback(); |
Slava Shklyaev | f034bf9 | 2020-02-11 14:27:02 +0000 | [diff] [blame] | 178 | Return<ErrorStatus> ret = preparedModel->execute_1_3(request, measure, deadline, {}, callback); |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 179 | EXPECT_TRUE(ret.isOk()); |
| 180 | EXPECT_EQ(ErrorStatus::NONE, ret.withDefault(ErrorStatus::GENERAL_FAILURE)); |
| 181 | if (!ret.isOk() || ret != ErrorStatus::NONE) return std::nullopt; |
| 182 | |
| 183 | // retrieve execution results |
| 184 | callback->wait(); |
| 185 | const ErrorStatus status = callback->getStatus(); |
| 186 | hidl_vec<OutputShape> outputShapes = callback->getOutputShapes(); |
| 187 | const Timing timing = callback->getTiming(); |
| 188 | |
| 189 | // return results |
| 190 | return Results{status, std::move(outputShapes), timing}; |
| 191 | } |
| 192 | |
| 193 | static MaybeResults executeSynchronously(const sp<IPreparedModel>& preparedModel, |
Michael Butler | 9b9a804 | 2020-02-13 16:37:22 -0800 | [diff] [blame] | 194 | const Request& request, |
| 195 | const OptionalTimePoint& deadline) { |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 196 | SCOPED_TRACE("synchronous"); |
| 197 | const MeasureTiming measure = MeasureTiming::NO; |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 198 | |
| 199 | // configure results callback |
| 200 | MaybeResults results; |
Michael Butler | c7327c5 | 2020-02-18 18:38:37 -0800 | [diff] [blame] | 201 | const auto cb = [&results](ErrorStatus status, const hidl_vec<OutputShape>& outputShapes, |
| 202 | const Timing& timing) { |
| 203 | results.emplace(status, outputShapes, timing); |
| 204 | }; |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 205 | |
| 206 | // run execution |
| 207 | const Return<void> ret = |
Slava Shklyaev | f034bf9 | 2020-02-11 14:27:02 +0000 | [diff] [blame] | 208 | preparedModel->executeSynchronously_1_3(request, measure, deadline, {}, cb); |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 209 | EXPECT_TRUE(ret.isOk()); |
| 210 | if (!ret.isOk()) return std::nullopt; |
| 211 | |
| 212 | // return results |
| 213 | return results; |
| 214 | } |
| 215 | |
| 216 | void runExecutionTest(const sp<IPreparedModel>& preparedModel, const TestModel& testModel, |
Xusong Wang | 41adc5b | 2020-02-25 11:43:10 -0800 | [diff] [blame^] | 217 | const Request& request, const ExecutionContext& context, bool synchronous, |
| 218 | DeadlineBoundType deadlineBound) { |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 219 | const ExecutionFunction execute = synchronous ? executeSynchronously : executeAsynchronously; |
Michael Butler | 9b9a804 | 2020-02-13 16:37:22 -0800 | [diff] [blame] | 220 | const auto deadline = makeDeadline(deadlineBound); |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 221 | |
| 222 | // Perform execution and unpack results. |
Michael Butler | 9b9a804 | 2020-02-13 16:37:22 -0800 | [diff] [blame] | 223 | const auto results = execute(preparedModel, request, deadline); |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 224 | if (!results.has_value()) return; |
| 225 | const auto& [status, outputShapes, timing] = results.value(); |
| 226 | |
| 227 | // Verify no timing information was returned |
| 228 | EXPECT_EQ(UINT64_MAX, timing.timeOnDevice); |
| 229 | EXPECT_EQ(UINT64_MAX, timing.timeInDriver); |
| 230 | |
| 231 | // Validate deadline information if applicable. |
| 232 | switch (deadlineBound) { |
| 233 | case DeadlineBoundType::NOW: |
Michael Butler | c7327c5 | 2020-02-18 18:38:37 -0800 | [diff] [blame] | 234 | case DeadlineBoundType::SHORT: |
| 235 | // Either the driver successfully completed the task or it |
| 236 | // aborted and returned MISSED_DEADLINE_*. |
Michael Butler | 9b9a804 | 2020-02-13 16:37:22 -0800 | [diff] [blame] | 237 | ASSERT_TRUE(status == ErrorStatus::NONE || |
| 238 | status == ErrorStatus::MISSED_DEADLINE_TRANSIENT || |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 239 | status == ErrorStatus::MISSED_DEADLINE_PERSISTENT); |
Michael Butler | c7327c5 | 2020-02-18 18:38:37 -0800 | [diff] [blame] | 240 | break; |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 241 | case DeadlineBoundType::UNLIMITED: |
| 242 | // If an unlimited deadline is supplied, we expect the execution to |
| 243 | // proceed normally. In this case, check it normally by breaking out |
| 244 | // of the switch statement. |
| 245 | ASSERT_EQ(ErrorStatus::NONE, status); |
| 246 | break; |
| 247 | } |
| 248 | |
| 249 | // If the model output operands are fully specified, outputShapes must be either |
| 250 | // 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] | 251 | ASSERT_TRUE(outputShapes.size() == 0 || |
| 252 | outputShapes.size() == testModel.main.outputIndexes.size()); |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 253 | |
| 254 | // Go through all outputs, check returned output shapes. |
| 255 | for (uint32_t i = 0; i < outputShapes.size(); i++) { |
| 256 | EXPECT_TRUE(outputShapes[i].isSufficient); |
Slava Shklyaev | 1f98e2e | 2020-01-31 15:14:24 +0000 | [diff] [blame] | 257 | const auto& expect = testModel.main.operands[testModel.main.outputIndexes[i]].dimensions; |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 258 | const std::vector<uint32_t> actual = outputShapes[i].dimensions; |
| 259 | EXPECT_EQ(expect, actual); |
| 260 | } |
| 261 | |
| 262 | // Retrieve execution results. |
| 263 | ASSERT_TRUE(nn::compliantWithV1_0(request)); |
| 264 | const V1_0::Request request10 = nn::convertToV1_0(request); |
Xusong Wang | 41adc5b | 2020-02-25 11:43:10 -0800 | [diff] [blame^] | 265 | const std::vector<TestBuffer> outputs = context.getOutputBuffers(request10); |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 266 | |
| 267 | // We want "close-enough" results. |
Michael Butler | c7327c5 | 2020-02-18 18:38:37 -0800 | [diff] [blame] | 268 | if (status == ErrorStatus::NONE) { |
| 269 | checkResults(testModel, outputs); |
| 270 | } |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 271 | } |
| 272 | |
| 273 | void runExecutionTests(const sp<IPreparedModel>& preparedModel, const TestModel& testModel, |
Xusong Wang | 41adc5b | 2020-02-25 11:43:10 -0800 | [diff] [blame^] | 274 | const Request& request, const ExecutionContext& context) { |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 275 | for (bool synchronous : {false, true}) { |
| 276 | for (auto deadlineBound : deadlineBounds) { |
Xusong Wang | 41adc5b | 2020-02-25 11:43:10 -0800 | [diff] [blame^] | 277 | runExecutionTest(preparedModel, testModel, request, context, synchronous, |
| 278 | deadlineBound); |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 279 | } |
| 280 | } |
| 281 | } |
| 282 | |
Michael Butler | 9b9a804 | 2020-02-13 16:37:22 -0800 | [diff] [blame] | 283 | void runTests(const sp<IDevice>& device, const TestModel& testModel) { |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 284 | // setup |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 285 | const Model model = createModel(testModel); |
| 286 | |
| 287 | // run prepare model tests |
Michael Butler | 9b9a804 | 2020-02-13 16:37:22 -0800 | [diff] [blame] | 288 | runPrepareModelTests(device, model); |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 289 | |
Michael Butler | 9b9a804 | 2020-02-13 16:37:22 -0800 | [diff] [blame] | 290 | // prepare model |
| 291 | sp<IPreparedModel> preparedModel; |
| 292 | createPreparedModel(device, model, &preparedModel); |
| 293 | if (preparedModel == nullptr) return; |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 294 | |
Michael Butler | 9b9a804 | 2020-02-13 16:37:22 -0800 | [diff] [blame] | 295 | // run execution tests |
Xusong Wang | 41adc5b | 2020-02-25 11:43:10 -0800 | [diff] [blame^] | 296 | ExecutionContext context; |
| 297 | const Request request = nn::convertToV1_3(context.createRequest(testModel)); |
| 298 | runExecutionTests(preparedModel, testModel, request, context); |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 299 | } |
| 300 | |
| 301 | class DeadlineTest : public GeneratedTestBase {}; |
| 302 | |
| 303 | TEST_P(DeadlineTest, Test) { |
Michael Butler | 9b9a804 | 2020-02-13 16:37:22 -0800 | [diff] [blame] | 304 | runTests(kDevice, kTestModel); |
Michael Butler | 616701d | 2020-01-07 14:52:44 -0800 | [diff] [blame] | 305 | } |
| 306 | |
| 307 | INSTANTIATE_GENERATED_TEST(DeadlineTest, |
| 308 | [](const TestModel& testModel) { return !testModel.expectFailure; }); |
| 309 | |
| 310 | } // namespace android::hardware::neuralnetworks::V1_3::vts::functional |