blob: 1fb03f4427add7591ace5efe104b1e9805037d90 [file] [log] [blame]
Kevin May42477c12020-03-26 13:34:14 +00001//
2// Copyright © 2020 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
Sadik Armagand7be72e2020-04-23 12:56:05 +01005// Note: the ArmnnFencedExecutionCallback and code snippet in the executeFenced() function
6// in this file is based on Android code
7// under the Apache 2.0 license. See comments below for details.
8//
Kevin May42477c12020-03-26 13:34:14 +00009
10#define LOG_TAG "ArmnnDriver"
11
12#include "ArmnnPreparedModel_1_3.hpp"
13#include "Utils.hpp"
14
15#include <Utils.h>
Sadik Armagand7be72e2020-04-23 12:56:05 +010016#include <android/sync.h>
Kevin May42477c12020-03-26 13:34:14 +000017#include <boost/format.hpp>
18#include <log/log.h>
19#include <OperationsUtils.h>
20#include <ExecutionBurstServer.h>
21#include <ValidateHal.h>
22
23#include <cassert>
24#include <cinttypes>
25
26using namespace android;
27using namespace android::hardware;
28
29namespace {
30
31static const Timing g_NoTiming = {.timeOnDevice = UINT64_MAX, .timeInDriver = UINT64_MAX};
32using namespace armnn_driver;
33using TimePoint = std::chrono::steady_clock::time_point;
34
35TimePoint Now()
36{
37 return std::chrono::steady_clock::now();
38}
39
40unsigned long MicrosecondsDuration(TimePoint endPoint, TimePoint startPoint)
41{
42 return static_cast<unsigned long>(std::chrono::duration_cast<std::chrono::microseconds>(
43 endPoint - startPoint).count());
44}
45
46void NotifyCallbackAndCheck(const ::android::sp<V1_0::IExecutionCallback>& callback,
47 V1_3::ErrorStatus errorStatus,
48 std::vector<OutputShape>,
49 const Timing,
50 std::string callingFunction)
51{
52 Return<void> returned = callback->notify(convertToV1_0(errorStatus));
53 // This check is required, if the callback fails and it isn't checked it will bring down the service
54 if (!returned.isOk())
55 {
56 ALOGE("ArmnnDriver::%s: hidl callback failed to return properly: %s",
57 callingFunction.c_str(), returned.description().c_str());
58 }
59}
60
61void NotifyCallbackAndCheck(const ::android::sp<V1_2::IExecutionCallback>& callback,
62 V1_3::ErrorStatus errorStatus,
63 std::vector<OutputShape> outputShapes,
64 const Timing timing,
65 std::string callingFunction)
66{
67 Return<void> returned = callback->notify_1_2(convertToV1_0(errorStatus), outputShapes, timing);
68 // This check is required, if the callback fails and it isn't checked it will bring down the service
69 if (!returned.isOk())
70 {
71 ALOGE("ArmnnDriver::%s: hidl callback failed to return properly: %s",
72 callingFunction.c_str(), returned.description().c_str());
73 }
74}
75
76void NotifyCallbackAndCheck(const ::android::sp<V1_3::IExecutionCallback>& callback,
77 V1_3::ErrorStatus errorStatus,
78 std::vector<OutputShape> outputShapes,
79 const Timing timing,
80 std::string callingFunction)
81{
82 Return<void> returned = callback->notify_1_3(errorStatus, outputShapes, timing);
83 // This check is required, if the callback fails and it isn't checked it will bring down the service
84 if (!returned.isOk())
85 {
86 ALOGE("ArmnnDriver::%s: hidl callback failed to return properly: %s",
87 callingFunction.c_str(), returned.description().c_str());
88 }
89}
90
91bool ValidateRequestArgument(const RequestArgument& requestArg, const armnn::TensorInfo& tensorInfo)
92{
93 if (requestArg.dimensions.size() != 0)
94 {
95 if (requestArg.dimensions.size() != tensorInfo.GetNumDimensions())
96 {
97 ALOGE("Mismatched dimensions (request argument: %zu, expected: %u)",
98 requestArg.dimensions.size(), tensorInfo.GetNumDimensions());
99 return false;
100 }
101
102 for (unsigned int d = 0; d < tensorInfo.GetNumDimensions(); ++d)
103 {
104 if (requestArg.dimensions[d] != tensorInfo.GetShape()[d])
105 {
106 ALOGE("Mismatched size for dimension %d (request argument: %u, expected %u)",
107 d, requestArg.dimensions[d], tensorInfo.GetShape()[d]);
108 return false;
109 }
110 }
111 }
112
113 return true;
114}
115
116armnn::Tensor GetTensorForRequestArgument(const RequestArgument& requestArg,
117 const armnn::TensorInfo& tensorInfo,
118 const std::vector<::android::nn::RunTimePoolInfo>& requestPools)
119{
120 if (!ValidateRequestArgument(requestArg, tensorInfo))
121 {
122 return armnn::Tensor();
123 }
124
125 return armnn::Tensor(tensorInfo, GetMemoryFromPool(requestArg.location, requestPools));
126}
127
128inline std::string BuildTensorName(const char* tensorNamePrefix, std::size_t index)
129{
130 return tensorNamePrefix + std::to_string(index);
131}
132
133} // anonymous namespace
134
135using namespace android::hardware;
136
137namespace armnn_driver
138{
139
140template<typename HalVersion>
141RequestThread<ArmnnPreparedModel_1_3, HalVersion, CallbackContext_1_3>
142 ArmnnPreparedModel_1_3<HalVersion>::m_RequestThread;
143
144template<typename HalVersion>
145template<typename TensorBindingCollection>
146void ArmnnPreparedModel_1_3<HalVersion>::DumpTensorsIfRequired(char const* tensorNamePrefix,
147 const TensorBindingCollection& tensorBindings)
148{
149 if (!m_RequestInputsAndOutputsDumpDir.empty())
150 {
151 const std::string requestName = boost::str(boost::format("%1%_%2%.dump") % m_NetworkId % m_RequestCount);
152 for (std::size_t i = 0u; i < tensorBindings.size(); ++i)
153 {
154 DumpTensor(m_RequestInputsAndOutputsDumpDir,
155 requestName,
156 BuildTensorName(tensorNamePrefix, i),
157 tensorBindings[i].second);
158 }
159 }
160}
161
162template<typename HalVersion>
163ArmnnPreparedModel_1_3<HalVersion>::ArmnnPreparedModel_1_3(armnn::NetworkId networkId,
164 armnn::IRuntime* runtime,
165 const V1_3::Model& model,
166 const std::string& requestInputsAndOutputsDumpDir,
167 const bool gpuProfilingEnabled)
168 : m_NetworkId(networkId)
169 , m_Runtime(runtime)
170 , m_Model(model)
171 , m_RequestCount(0)
172 , m_RequestInputsAndOutputsDumpDir(requestInputsAndOutputsDumpDir)
173 , m_GpuProfilingEnabled(gpuProfilingEnabled)
174{
175 // Enable profiling if required.
176 m_Runtime->GetProfiler(m_NetworkId)->EnableProfiling(m_GpuProfilingEnabled);
177}
178
179template<typename HalVersion>
180ArmnnPreparedModel_1_3<HalVersion>::~ArmnnPreparedModel_1_3()
181{
182 // Get a hold of the profiler used by this model.
183 std::shared_ptr<armnn::IProfiler> profiler = m_Runtime->GetProfiler(m_NetworkId);
184
185 // Unload the network associated with this model.
186 m_Runtime->UnloadNetwork(m_NetworkId);
187
188 // Dump the profiling info to a file if required.
189 DumpJsonProfilingIfRequired(m_GpuProfilingEnabled, m_RequestInputsAndOutputsDumpDir, m_NetworkId, profiler.get());
190}
191
192template<typename HalVersion>
193Return <V1_0::ErrorStatus> ArmnnPreparedModel_1_3<HalVersion>::execute(const V1_0::Request& request,
194 const ::android::sp<V1_0::IExecutionCallback>& callback)
195{
196 if (callback.get() == nullptr)
197 {
198 ALOGE("ArmnnPreparedModel_1_3::execute invalid callback passed");
199 return V1_0::ErrorStatus::INVALID_ARGUMENT;
200 }
201
202 auto cb = [callback](V1_3::ErrorStatus errorStatus,
203 std::vector<OutputShape> outputShapes,
204 const Timing& timing,
205 std::string callingFunction)
206 {
207 NotifyCallbackAndCheck(callback, errorStatus, outputShapes, timing, callingFunction);
208 };
209
210
211 return convertToV1_0(Execute(convertToV1_3(request), MeasureTiming::NO, cb));
212}
213
214template<typename HalVersion>
215Return <V1_0::ErrorStatus> ArmnnPreparedModel_1_3<HalVersion>::execute_1_2(
216 const V1_0::Request& request,
217 MeasureTiming measureTiming,
218 const sp<V1_2::IExecutionCallback>& callback)
219{
220 if (callback.get() == nullptr)
221 {
222 ALOGE("ArmnnPreparedModel_1_3::execute_1_2 invalid callback passed");
223 return V1_0::ErrorStatus::INVALID_ARGUMENT;
224 }
225
226 auto cb = [callback](V1_3::ErrorStatus errorStatus,
227 std::vector<OutputShape> outputShapes,
228 const Timing& timing,
229 std::string callingFunction)
230 {
231 NotifyCallbackAndCheck(callback, errorStatus, outputShapes, timing, callingFunction);
232 };
233
234 return convertToV1_0(Execute(convertToV1_3(request), measureTiming, cb));
235}
236
237template<typename HalVersion>
238Return <V1_3::ErrorStatus> ArmnnPreparedModel_1_3<HalVersion>::execute_1_3(
239 const V1_3::Request& request,
240 MeasureTiming measureTiming,
241 const V1_3::OptionalTimePoint&,
Kevin May352d8382020-03-31 15:03:42 +0100242 const V1_3::OptionalTimeoutDuration&,
Kevin May42477c12020-03-26 13:34:14 +0000243 const sp<V1_3::IExecutionCallback>& callback)
244{
245 if (callback.get() == nullptr)
246 {
247 ALOGE("ArmnnPreparedModel_1_3::execute_1_3 invalid callback passed");
248 return V1_3::ErrorStatus::INVALID_ARGUMENT;
249 }
250
251 auto cb = [callback](V1_3::ErrorStatus errorStatus,
252 std::vector<OutputShape> outputShapes,
253 const Timing& timing,
254 std::string callingFunction)
255 {
256 NotifyCallbackAndCheck(callback, errorStatus, outputShapes, timing, callingFunction);
257 };
258
259 return Execute(request, measureTiming, cb);
260}
261
Sadik Armagand7be72e2020-04-23 12:56:05 +0100262/// This class is inspired by the sample implementation in Android named SampleFencedExecutionCallback.
263/// The original code is licensed under Apache-2.0 and can be found at the following link:
264/// https://android.googlesource.com/platform/frameworks/ml/+/master/nn/driver/sample/SampleDriver.h
265class ArmnnFencedExecutionCallback : public V1_3::IFencedExecutionCallback
266{
267public:
268 ArmnnFencedExecutionCallback(V1_3::ErrorStatus errorStatus, Timing timing, Timing fenceTiming)
269 : m_ErrorStatus(errorStatus), m_Timing(timing), m_FenceTiming(fenceTiming) {}
270 ~ArmnnFencedExecutionCallback() {}
271
272 Return<void> getExecutionInfo(getExecutionInfo_cb callback) override
273 {
274 callback(m_ErrorStatus, m_Timing, m_FenceTiming);
275 return Void();
276 }
277private:
278 V1_3::ErrorStatus m_ErrorStatus;
279 Timing m_Timing;
280 Timing m_FenceTiming;
281};
282
Kevin May42477c12020-03-26 13:34:14 +0000283template<typename HalVersion>
Sadik Armagand7be72e2020-04-23 12:56:05 +0100284Return<void> ArmnnPreparedModel_1_3<HalVersion>::executeFenced(const V1_3::Request& request,
285 const hidl_vec<hidl_handle>& fenceWaitFor,
286 MeasureTiming measureTiming,
Sadik Armagan7b9ce8d2020-04-21 10:39:28 +0100287 const OptionalTimePoint& deadline,
288 const OptionalTimeoutDuration& loopTimeoutDuration,
Kevin May352d8382020-03-31 15:03:42 +0100289 const OptionalTimeoutDuration&,
Kevin May42477c12020-03-26 13:34:14 +0000290 executeFenced_cb cb)
291{
Sadik Armagan7b9ce8d2020-04-21 10:39:28 +0100292 ALOGV("ArmnnPreparedModel_1_3::executeFenced(...)");
293 if (cb == nullptr)
294 {
295 ALOGE("ArmnnPreparedModel_1_3::executeFenced invalid callback passed");
296 cb(ErrorStatus::INVALID_ARGUMENT, hidl_handle(nullptr), nullptr);
297 return Void();
298 }
299
300 if (deadline.getDiscriminator() != OptionalTimePoint::hidl_discriminator::none)
301 {
302 ALOGW("ArmnnPreparedModel_1_3::executeFenced parameter deadline is set but not supported.");
303 }
304
305 if (loopTimeoutDuration.getDiscriminator() != OptionalTimeoutDuration::hidl_discriminator::none)
306 {
307 ALOGW("ArmnnPreparedModel_1_3::executeFenced parameter loopTimeoutDuration is set but not supported.");
308 }
309
Sadik Armagand7be72e2020-04-23 12:56:05 +0100310 ExecutionContext_1_3 ctx;
311 if (measureTiming == MeasureTiming::YES)
312 {
313 ctx.measureTimings = measureTiming;
314 ctx.driverStart = Now();
315 }
316
317 ALOGV("ArmnnPreparedModel_1_3::executeFenced(): %s", GetModelSummary(m_Model).c_str());
318 m_RequestCount++;
319
320 if (!android::nn::validateRequest(request, m_Model))
321 {
322 cb(ErrorStatus::INVALID_ARGUMENT, hidl_handle(nullptr), nullptr);
323 return Void();
324 }
325
326 if (!m_RequestInputsAndOutputsDumpDir.empty())
327 {
328 ALOGD("Dumping inputs and outputs for request %" PRIuPTR, reinterpret_cast<std::uintptr_t>(&cb));
329 }
330
331 // This code snippet is inspired by the sample implementation in Android named SampleDriver::executeFenced()
332 // function. The original code is licensed under Apache-2.0 and can be found at the following link:
333 // https://android.googlesource.com/platform/frameworks/ml/+/master/nn/driver/sample/SampleDriver.cpp
334 const auto fenceSize = fenceWaitFor.size();
335 for (unsigned int index = 0; index < fenceSize; ++index)
336 {
337 auto fenceNativeHandle = fenceWaitFor[index].getNativeHandle();
338 if (!fenceNativeHandle)
339 {
340 cb(ErrorStatus::INVALID_ARGUMENT, hidl_handle(nullptr), nullptr);
341 return Void();
342 }
343
344 if (sync_wait(fenceNativeHandle->data[0], -1) < 0)
345 {
346 ALOGE("ArmnnPreparedModel_1_3::executeFenced sync fence failed.");
347 cb(ErrorStatus::GENERAL_FAILURE, hidl_handle(nullptr), nullptr);
348 return Void();
349 }
350 }
351
352 TimePoint fenceExecutionStart;
353 if (measureTiming == MeasureTiming::YES)
354 {
355 fenceExecutionStart = Now();
356 }
357
358 // map the memory pool into shared pointers
359 // use a shared memory pools vector on the heap, as it is passed to the request thread
360 auto memPools = std::make_shared<std::vector<android::nn::RunTimePoolInfo>>();
361
362 // allocate the tensors on the heap, as they are passed to the request thread
363 auto inputs = std::make_shared<armnn::InputTensors>();
364 auto outputs = std::make_shared<armnn::OutputTensors>();
365
366 auto [status, outShapes, timings, message] = PrepareMemoryForIO(*inputs, *outputs, *memPools, request);
367 if (status != V1_3::ErrorStatus::NONE)
368 {
369 cb(ErrorStatus::INVALID_ARGUMENT, hidl_handle(nullptr), nullptr);
370 return Void();
371 }
372
373 ALOGV("ArmnnPreparedModel_1_3::executeFenced(...) before ExecuteGraph");
374
375 // call it with nullCallback for now as we will report the error status from here..
376 auto nullCallback = [](V1_3::ErrorStatus, std::vector<OutputShape>, const Timing&, std::string) {};
377 CallbackContext_1_3 cbCtx;
378 cbCtx.callback = nullCallback;
379 cbCtx.ctx = ctx;
380
381 auto errorStatus = ExecuteGraph(memPools, *inputs, *outputs, cbCtx);
382 if (errorStatus != V1_3::ErrorStatus::NONE)
383 {
384 cb(errorStatus, hidl_handle(nullptr), nullptr);
385 return Void();
386 }
387 ALOGV("ArmnnPreparedModel_1_3::executeFenced(...) after ExecuteGraph");
388
389 Timing timing = g_NoTiming;
390 Timing fenceTiming = g_NoTiming;
391 if (measureTiming == MeasureTiming::YES)
392 {
Sadik Armagand7be72e2020-04-23 12:56:05 +0100393 fenceTiming.timeOnDevice = MicrosecondsDuration(ctx.deviceEnd, ctx.deviceStart);
Kevin May949a69e2020-04-24 10:21:40 +0100394 fenceTiming.timeInDriver = MicrosecondsDuration(ctx.driverEnd, fenceExecutionStart);
395 ALOGV("ArmnnPreparedModel_1_3::fenceFinishExecutionTiming - Device = %lu Driver = %lu",
Sadik Armagand7be72e2020-04-23 12:56:05 +0100396 fenceTiming.timeOnDevice, fenceTiming.timeInDriver);
397 }
398
399 sp<ArmnnFencedExecutionCallback> armnnFencedExecutionCallback =
400 new ArmnnFencedExecutionCallback(ErrorStatus::NONE, timing, fenceTiming);
401 cb(ErrorStatus::NONE, hidl_handle(nullptr), armnnFencedExecutionCallback);
Kevin May42477c12020-03-26 13:34:14 +0000402 return Void();
403}
404
405template<typename HalVersion>
406Return<V1_3::ErrorStatus> ArmnnPreparedModel_1_3<HalVersion>::PrepareMemoryForInputs(
407 armnn::InputTensors& inputs,
408 const V1_3::Request& request,
409 const std::vector<android::nn::RunTimePoolInfo>& memPools)
410{
411 inputs.reserve(request.inputs.size());
412 for (unsigned int i = 0; i < request.inputs.size(); i++)
413 {
414 const auto& inputArg = request.inputs[i];
415
416 const armnn::TensorInfo inputTensorInfo = m_Runtime->GetInputTensorInfo(m_NetworkId, i);
417 const armnn::Tensor inputTensor = GetTensorForRequestArgument(inputArg, inputTensorInfo, memPools);
418
419 if (inputTensor.GetMemoryArea() == nullptr)
420 {
421 ALOGE("Cannot execute request. Error converting request input %u to tensor", i);
422 return V1_3::ErrorStatus::GENERAL_FAILURE;
423 }
424
425 inputs.emplace_back(i, inputTensor);
426 }
427
428 return V1_3::ErrorStatus::NONE;
429}
430
431template<typename HalVersion>
432Return<V1_3::ErrorStatus> ArmnnPreparedModel_1_3<HalVersion>::PrepareMemoryForOutputs(
433 armnn::OutputTensors& outputs,
434 std::vector<OutputShape> &outputShapes,
435 const V1_3::Request& request,
436 const std::vector<android::nn::RunTimePoolInfo>& memPools)
437{
438 outputs.reserve(request.outputs.size());
439 for (unsigned int i = 0; i < request.outputs.size(); i++)
440 {
441 const auto& outputArg = request.outputs[i];
442
443 const armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkId, i);
444 const armnn::Tensor outputTensor = GetTensorForRequestArgument(outputArg, outputTensorInfo, memPools);
445 if (outputTensor.GetMemoryArea() == nullptr)
446 {
447 ALOGE("Cannot execute request. Error converting request output %u to tensor", i);
448 return V1_3::ErrorStatus::GENERAL_FAILURE;
449 }
450
451 const size_t outputSize = outputTensorInfo.GetNumBytes();
452 const size_t bufferSize = memPools.at(outputArg.location.poolIndex).getHidlMemory().size();
453 if (bufferSize < outputSize)
454 {
455 ALOGW("ArmnnPreparedModel_1_3::Execute failed");
456 return V1_3::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
457 }
458
459 outputs.emplace_back(i, outputTensor);
460 outputShapes[i] = ComputeShape(outputTensorInfo);
461 }
462
463 return V1_3::ErrorStatus::NONE;
464}
465
466template<typename HalVersion>
467std::tuple<V1_3::ErrorStatus, hidl_vec<OutputShape>, Timing, std::string>
468 ArmnnPreparedModel_1_3<HalVersion>::PrepareMemoryForIO(armnn::InputTensors& inputs,
469 armnn::OutputTensors& outputs,
470 std::vector<android::nn::RunTimePoolInfo>& memPools,
471 const V1_3::Request& request)
472{
473 if (!setRunTimePoolInfosFromMemoryPools(&memPools, request.pools))
474 {
Sadik Armaganef8a3932020-04-09 17:21:50 +0100475 return {ErrorStatus::INVALID_ARGUMENT, {}, g_NoTiming, "ArmnnPreparedModel_1_3::execute"};
Kevin May42477c12020-03-26 13:34:14 +0000476 }
477
478 // add the inputs and outputs with their data
479 try
480 {
481 if (PrepareMemoryForInputs(inputs, request, memPools) != V1_3::ErrorStatus::NONE)
482 {
483 return {ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_3::execute"};
484 }
485
486 std::vector<OutputShape> outputShapes(request.outputs.size());
487
488 auto errorStatus = PrepareMemoryForOutputs(outputs, outputShapes, request, memPools);
489 if (errorStatus != V1_3::ErrorStatus::NONE)
490 {
491 return {errorStatus, outputShapes, g_NoTiming, "ArmnnPreparedModel_1_3::execute"};
492 }
493 }
494 catch (armnn::Exception& e)
495 {
496 ALOGW("armnn::Exception caught while preparing for EnqueueWorkload: %s", e.what());
497 return {ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_3::execute"};
498 }
499 catch (std::exception& e)
500 {
501 ALOGE("std::exception caught while preparing for EnqueueWorkload: %s", e.what());
502 return {ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_3::execute"};
503 }
504
505 return {V1_3::ErrorStatus::NONE, {}, g_NoTiming, "ArmnnPreparedModel_1_3::execute"};
506}
507
508template<typename HalVersion>
509template<typename CallbackContext>
510Return<void> ArmnnPreparedModel_1_3<HalVersion>::ExecuteSynchronously(const V1_3::Request& request,
511 CallbackContext cbCtx)
512{
513 if (cbCtx.ctx.measureTimings == MeasureTiming::YES)
514 {
515 cbCtx.ctx.driverStart = Now();
516 }
517
518 if (!android::nn::validateRequest(convertToV1_3(request), m_Model))
519 {
520 ALOGE("ArmnnPreparedModel_1_3::ExecuteSynchronously invalid request model");
521 cbCtx.callback(V1_3::ErrorStatus::INVALID_ARGUMENT,
522 {},
523 g_NoTiming,
524 "ArmnnPreparedModel_1_3::ExecuteSynchronously invalid request model");
525 return Void();
526 }
527
528 if (!android::nn::validateRequest(request, m_Model))
529 {
530 ALOGE("ArmnnPreparedModel_1_3::ExecuteSynchronously invalid request model");
531 cbCtx.callback(V1_3::ErrorStatus::INVALID_ARGUMENT,
532 {},
533 g_NoTiming,
534 "ArmnnPreparedModel_1_3::ExecuteSynchronously invalid request model");
Sadik Armaganef8a3932020-04-09 17:21:50 +0100535 return Void();
Kevin May42477c12020-03-26 13:34:14 +0000536 }
537
538
539 // map the memory pool into shared pointers
540 // use a shared memory pools vector on the heap, as it is passed to the request thread
541 auto memPools = std::make_shared<std::vector<android::nn::RunTimePoolInfo>>();
542
543 // allocate the tensors on the heap, as they are passed to the request thread
544 auto inputs = std::make_shared<armnn::InputTensors>();
545 auto outputs = std::make_shared<armnn::OutputTensors>();
546
547 auto [status, outputShapes, timing, message] = PrepareMemoryForIO(*inputs, *outputs, *memPools, request);
548 if (status != V1_3::ErrorStatus::NONE)
549 {
550 cbCtx.callback(status, outputShapes, timing, message);
Sadik Armaganef8a3932020-04-09 17:21:50 +0100551 return Void();
Kevin May42477c12020-03-26 13:34:14 +0000552 }
553
554 ALOGV("ArmnnPreparedModel_1_3::ExecuteSynchronously() before Execution");
555
556 ExecuteGraph(memPools, *inputs, *outputs, cbCtx);
557 return Void();
558}
559
560template<typename HalVersion>
561Return<void> ArmnnPreparedModel_1_3<HalVersion>::executeSynchronously(const V1_0::Request& request,
562 MeasureTiming measureTiming,
563 executeSynchronously_cb cb)
564{
565 ALOGV("ArmnnPreparedModel_1_3::executeSynchronously(): %s", GetModelSummary(m_Model).c_str());
566 m_RequestCount++;
567
568 if (cb == nullptr)
569 {
570 ALOGE("ArmnnPreparedModel_1_3::executeSynchronously invalid callback passed");
571 return Void();
572 }
573
574 auto cbWrapper = [cb](V1_3::ErrorStatus errorStatus,
575 std::vector<OutputShape> outputShapes,
576 const Timing& timing,
577 std::string)
578 {
579 cb(convertToV1_0(errorStatus), outputShapes, timing);
580 };
581
582 CallbackContext_1_3 cbCtx;
583 cbCtx.callback = cbWrapper;
584 cbCtx.ctx.measureTimings = measureTiming;
585
586 ExecuteSynchronously(convertToV1_3(request), cbCtx);
587 return Void();
588}
589
590template<typename HalVersion>
Kevin May352d8382020-03-31 15:03:42 +0100591Return<void> ArmnnPreparedModel_1_3<HalVersion>::executeSynchronously_1_3(
592 const V1_3::Request& request,
593 MeasureTiming measureTiming,
594 const V1_3::OptionalTimePoint& deadline,
595 const V1_3::OptionalTimeoutDuration& loopTimeoutDuration,
596 executeSynchronously_1_3_cb cb)
Kevin May42477c12020-03-26 13:34:14 +0000597{
598 ALOGV("ArmnnPreparedModel_1_3::executeSynchronously_1_3(): %s", GetModelSummary(m_Model).c_str());
599 m_RequestCount++;
600
601 if (cb == nullptr)
602 {
603 ALOGE("ArmnnPreparedModel_1_3::executeSynchronously_1_3 invalid callback passed");
604 return Void();
605 }
606
607 if (deadline.getDiscriminator() != OptionalTimePoint::hidl_discriminator::none)
608 {
Sadik Armagan7b9ce8d2020-04-21 10:39:28 +0100609 ALOGW("ArmnnPreparedModel_1_3::executeSynchronously_1_3 parameter deadline is set but not supported.");
Kevin May42477c12020-03-26 13:34:14 +0000610 }
611
Kevin May352d8382020-03-31 15:03:42 +0100612 if (loopTimeoutDuration.getDiscriminator() != OptionalTimeoutDuration::hidl_discriminator::none)
Sadik Armagan7b9ce8d2020-04-21 10:39:28 +0100613 {
614 ALOGW(
615 "ArmnnPreparedModel_1_3::executeSynchronously_1_3 parameter loopTimeoutDuration is set but not supported.");
Kevin May352d8382020-03-31 15:03:42 +0100616 }
617
Kevin May42477c12020-03-26 13:34:14 +0000618 auto cbWrapper = [cb](V1_3::ErrorStatus errorStatus,
619 std::vector<OutputShape> outputShapes,
620 const Timing& timing,
621 std::string)
622 {
623 cb(errorStatus, outputShapes, timing);
624 };
625
626 CallbackContext_1_3 cbCtx;
627 cbCtx.callback = cbWrapper;
628 cbCtx.ctx.measureTimings = measureTiming;
629
630 ExecuteSynchronously(request, cbCtx);
631 return Void();
632}
633
634template<typename HalVersion>
635Return<void> ArmnnPreparedModel_1_3<HalVersion>::configureExecutionBurst(
636 const sp<V1_2::IBurstCallback>& callback,
637 const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
638 const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
639 V1_3::IPreparedModel::configureExecutionBurst_cb cb)
640{
641 ALOGV("ArmnnPreparedModel_1_3::configureExecutionBurst");
642 const sp<V1_2::IBurstContext> burst = ExecutionBurstServer::create(callback,
643 requestChannel,
644 resultChannel,
645 this);
646
647 if (burst == nullptr)
648 {
649 cb(V1_0::ErrorStatus::GENERAL_FAILURE, {});
650 }
651 else
652 {
653 cb(V1_0::ErrorStatus::NONE, burst);
654 }
655 return Void();
656}
657
658template<typename HalVersion>
659template<typename CallbackContext>
Sadik Armagand7be72e2020-04-23 12:56:05 +0100660Return <V1_3::ErrorStatus> ArmnnPreparedModel_1_3<HalVersion>::ExecuteGraph(
Kevin May42477c12020-03-26 13:34:14 +0000661 std::shared_ptr<std::vector<::android::nn::RunTimePoolInfo>>& pMemPools,
662 armnn::InputTensors& inputTensors,
663 armnn::OutputTensors& outputTensors,
664 CallbackContext cb)
665{
666 ALOGV("ArmnnPreparedModel_1_3::ExecuteGraph(...)");
667
Kevin May42477c12020-03-26 13:34:14 +0000668 DumpTensorsIfRequired("Input", inputTensors);
669
670 std::vector<OutputShape> outputShapes(outputTensors.size());
671 for (unsigned int i = 0; i < outputTensors.size(); i++)
672 {
673 std::pair<int, armnn::Tensor> outputTensorPair = outputTensors[i];
674 const armnn::Tensor outputTensor = outputTensorPair.second;
675 const armnn::TensorInfo outputTensorInfo = outputTensor.GetInfo();
676
677 outputShapes[i] = ComputeShape(outputTensorInfo);
678 }
679
680 // run it
681 try
682 {
683 if (cb.ctx.measureTimings == MeasureTiming::YES)
684 {
Sadik Armagand7be72e2020-04-23 12:56:05 +0100685 cb.ctx.deviceStart = Now();
Kevin May42477c12020-03-26 13:34:14 +0000686 }
687
688 armnn::Status status = m_Runtime->EnqueueWorkload(m_NetworkId, inputTensors, outputTensors);
689
690 if (cb.ctx.measureTimings == MeasureTiming::YES)
691 {
Sadik Armagand7be72e2020-04-23 12:56:05 +0100692 cb.ctx.deviceEnd = Now();
Kevin May42477c12020-03-26 13:34:14 +0000693 }
694 if (status != armnn::Status::Success)
695 {
696 ALOGW("EnqueueWorkload failed");
Sadik Armagand7be72e2020-04-23 12:56:05 +0100697 cb.callback(V1_3::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_3::ExecuteGraph");
698 return V1_3::ErrorStatus::GENERAL_FAILURE;
Kevin May42477c12020-03-26 13:34:14 +0000699 }
700 }
701 catch (armnn::Exception& e)
702 {
703 ALOGW("armnn:Exception caught from EnqueueWorkload: %s", e.what());
704 cb.callback(V1_3::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_3::ExecuteGraph");
Sadik Armagand7be72e2020-04-23 12:56:05 +0100705 return V1_3::ErrorStatus::GENERAL_FAILURE;
Kevin May42477c12020-03-26 13:34:14 +0000706 }
707 catch (std::exception& e)
708 {
709 ALOGE("std::exception caught from EnqueueWorkload: %s", e.what());
710 cb.callback(V1_3::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_3::ExecuteGraph");
Sadik Armagand7be72e2020-04-23 12:56:05 +0100711 return V1_3::ErrorStatus::GENERAL_FAILURE;
Kevin May42477c12020-03-26 13:34:14 +0000712 }
713
714 CommitPools(*pMemPools);
715
716 DumpTensorsIfRequired("Output", outputTensors);
717
718 if (cb.ctx.measureTimings == MeasureTiming::YES)
719 {
Kevin May949a69e2020-04-24 10:21:40 +0100720 cb.ctx.driverEnd = Now();
Kevin May42477c12020-03-26 13:34:14 +0000721 Timing timing;
Sadik Armagand7be72e2020-04-23 12:56:05 +0100722 timing.timeOnDevice = MicrosecondsDuration(cb.ctx.deviceEnd, cb.ctx.deviceStart);
Kevin May949a69e2020-04-24 10:21:40 +0100723 timing.timeInDriver = MicrosecondsDuration(cb.ctx.driverEnd, cb.ctx.driverStart);
724 ALOGV("ArmnnPreparedModel_1_3::execute timing - Device = %lu Driver = %lu", timing.timeOnDevice,
Kevin May42477c12020-03-26 13:34:14 +0000725 timing.timeInDriver);
726 cb.callback(V1_3::ErrorStatus::NONE, outputShapes, timing, "ArmnnPreparedModel_1_3::ExecuteGraph");
Sadik Armagand7be72e2020-04-23 12:56:05 +0100727 } else
728 {
Kevin May42477c12020-03-26 13:34:14 +0000729 cb.callback(V1_3::ErrorStatus::NONE, outputShapes, g_NoTiming, "ArmnnPreparedModel_1_3::ExecuteGraph");
730 }
Sadik Armagand7be72e2020-04-23 12:56:05 +0100731 return V1_3::ErrorStatus::NONE;
Kevin May42477c12020-03-26 13:34:14 +0000732}
733
734template<typename HalVersion>
735bool ArmnnPreparedModel_1_3<HalVersion>::ExecuteWithDummyInputs()
736{
737 std::vector<std::vector<char>> storage;
738 armnn::InputTensors inputTensors;
739 for (unsigned int i = 0; i < getMainModel(m_Model).inputIndexes.size(); i++)
740 {
741 const armnn::TensorInfo inputTensorInfo = m_Runtime->GetInputTensorInfo(m_NetworkId, i);
742 storage.emplace_back(inputTensorInfo.GetNumBytes());
743 const armnn::ConstTensor inputTensor(inputTensorInfo, storage.back().data());
744
745 inputTensors.emplace_back(i, inputTensor);
746 }
747
748 armnn::OutputTensors outputTensors;
749 for (unsigned int i = 0; i < getMainModel(m_Model).outputIndexes.size(); i++)
750 {
751 const armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkId, i);
752 storage.emplace_back(outputTensorInfo.GetNumBytes());
753 const armnn::Tensor outputTensor(outputTensorInfo, storage.back().data());
754
755 outputTensors.emplace_back(i, outputTensor);
756 }
757
758 auto nullCallback = [](V1_3::ErrorStatus, std::vector<OutputShape>, const Timing&, std::string) {};
759 CallbackContext_1_3 callbackContext;
760 callbackContext.callback = nullCallback;
761 callbackContext.ctx.measureTimings = MeasureTiming::NO;
762 auto memPools = std::make_shared<std::vector<::android::nn::RunTimePoolInfo>>();
Sadik Armagand7be72e2020-04-23 12:56:05 +0100763
764 auto errorStatus = ExecuteGraph(memPools,
765 inputTensors,
766 outputTensors,
767 callbackContext);
768 return errorStatus == V1_3::ErrorStatus::NONE;
Kevin May42477c12020-03-26 13:34:14 +0000769}
770
771template<typename HalVersion>
772Return <V1_3::ErrorStatus> ArmnnPreparedModel_1_3<HalVersion>::Execute(const V1_3::Request& request,
773 MeasureTiming measureTiming,
774 CallbackAsync_1_3 callback)
775{
776 ExecutionContext_1_3 ctx;
777 if (measureTiming == MeasureTiming::YES)
778 {
779 ctx.measureTimings = measureTiming;
780 ctx.driverStart = Now();
781 }
782
783 ALOGV("ArmnnPreparedModel_1_3::execute(): %s", GetModelSummary(m_Model).c_str());
784 m_RequestCount++;
785
786 if (!android::nn::validateRequest(request, m_Model))
787 {
788 callback(V1_3::ErrorStatus::INVALID_ARGUMENT, {}, g_NoTiming, "ArmnnPreparedModel_1_3::execute");
789 return V1_3::ErrorStatus::INVALID_ARGUMENT;
790 }
791
792 if (!m_RequestInputsAndOutputsDumpDir.empty())
793 {
794 ALOGD("Dumping inputs and outputs for request %" PRIuPTR, reinterpret_cast<std::uintptr_t>(&callback));
795 }
796
797 // map the memory pool into shared pointers
798 // use a shared memory pools vector on the heap, as it is passed to the request thread
799 auto memPools = std::make_shared<std::vector<android::nn::RunTimePoolInfo>>();
800
801 // allocate the tensors on the heap, as they are passed to the request thread
802 auto inputTensors = std::make_shared<armnn::InputTensors>();
803 auto outputTensors = std::make_shared<armnn::OutputTensors>();
804
805 auto [status, outShapes, timing, message] = PrepareMemoryForIO(*inputTensors, *outputTensors,
806 *memPools, request);
807 if (status != V1_3::ErrorStatus::NONE)
808 {
809 callback(status, outShapes, timing, message);
810 }
811
812 switch(status)
813 {
814 case V1_3::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE:
815 return V1_3::ErrorStatus::NONE;
816 case V1_3::ErrorStatus::GENERAL_FAILURE:
817 return V1_3::ErrorStatus::GENERAL_FAILURE;
818 default:
819 {}
820 }
821
822 ALOGV("ArmnnPreparedModel_1_3::execute(...) before PostMsg");
823
824 // post the request for asynchronous execution
825 CallbackContext_1_3 cb;
826 cb.callback = callback;
827 cb.ctx = ctx;
828 m_RequestThread.PostMsg(this, memPools, inputTensors, outputTensors, cb);
829 ALOGV("ArmnnPreparedModel_1_3::execute(...) after PostMsg");
830 return V1_3::ErrorStatus::NONE;
831}
832
833#ifdef ARMNN_ANDROID_NN_V1_3
834template class ArmnnPreparedModel_1_3<hal_1_3::HalPolicy>;
Sadik Armagand7be72e2020-04-23 12:56:05 +0100835template Return <V1_3::ErrorStatus> ArmnnPreparedModel_1_3<hal_1_3::HalPolicy>::ExecuteGraph<CallbackContext_1_3>(
Kevin May42477c12020-03-26 13:34:14 +0000836 std::shared_ptr<std::vector<::android::nn::RunTimePoolInfo>>& pMemPools,
837 armnn::InputTensors& pInputTensors,
838 armnn::OutputTensors& pOutputTensors,
839 CallbackContext_1_3 cb);
840#endif
841
842} // namespace armnn_driver