blob: 386cc1746e0a9da5cc200550eb4c01d6cd7466f4 [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 {
Finn Williamsa4983ce2020-07-23 12:55:12 +0100104 if (requestArg.dimensions[d] != 0 && requestArg.dimensions[d] != tensorInfo.GetShape()[d])
Kevin May42477c12020-03-26 13:34:14 +0000105 {
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>
Narumol Prangnawaratcad4e912020-06-02 12:07:43 +0100141RequestThread_1_3<ArmnnPreparedModel_1_3, HalVersion, CallbackContext_1_3>
Kevin May42477c12020-03-26 13:34:14 +0000142 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,
Narumol Prangnawaratcad4e912020-06-02 12:07:43 +0100167 const bool gpuProfilingEnabled,
168 V1_3::Priority priority)
Kevin May42477c12020-03-26 13:34:14 +0000169 : m_NetworkId(networkId)
170 , m_Runtime(runtime)
171 , m_Model(model)
172 , m_RequestCount(0)
173 , m_RequestInputsAndOutputsDumpDir(requestInputsAndOutputsDumpDir)
174 , m_GpuProfilingEnabled(gpuProfilingEnabled)
Narumol Prangnawaratcad4e912020-06-02 12:07:43 +0100175 , m_ModelPriority(priority)
Kevin May42477c12020-03-26 13:34:14 +0000176{
177 // Enable profiling if required.
178 m_Runtime->GetProfiler(m_NetworkId)->EnableProfiling(m_GpuProfilingEnabled);
179}
180
181template<typename HalVersion>
182ArmnnPreparedModel_1_3<HalVersion>::~ArmnnPreparedModel_1_3()
183{
184 // Get a hold of the profiler used by this model.
185 std::shared_ptr<armnn::IProfiler> profiler = m_Runtime->GetProfiler(m_NetworkId);
186
187 // Unload the network associated with this model.
188 m_Runtime->UnloadNetwork(m_NetworkId);
189
190 // Dump the profiling info to a file if required.
191 DumpJsonProfilingIfRequired(m_GpuProfilingEnabled, m_RequestInputsAndOutputsDumpDir, m_NetworkId, profiler.get());
192}
193
194template<typename HalVersion>
195Return <V1_0::ErrorStatus> ArmnnPreparedModel_1_3<HalVersion>::execute(const V1_0::Request& request,
196 const ::android::sp<V1_0::IExecutionCallback>& callback)
197{
198 if (callback.get() == nullptr)
199 {
200 ALOGE("ArmnnPreparedModel_1_3::execute invalid callback passed");
201 return V1_0::ErrorStatus::INVALID_ARGUMENT;
202 }
203
204 auto cb = [callback](V1_3::ErrorStatus errorStatus,
205 std::vector<OutputShape> outputShapes,
206 const Timing& timing,
207 std::string callingFunction)
208 {
209 NotifyCallbackAndCheck(callback, errorStatus, outputShapes, timing, callingFunction);
210 };
211
212
213 return convertToV1_0(Execute(convertToV1_3(request), MeasureTiming::NO, cb));
214}
215
216template<typename HalVersion>
217Return <V1_0::ErrorStatus> ArmnnPreparedModel_1_3<HalVersion>::execute_1_2(
218 const V1_0::Request& request,
219 MeasureTiming measureTiming,
220 const sp<V1_2::IExecutionCallback>& callback)
221{
222 if (callback.get() == nullptr)
223 {
224 ALOGE("ArmnnPreparedModel_1_3::execute_1_2 invalid callback passed");
225 return V1_0::ErrorStatus::INVALID_ARGUMENT;
226 }
227
228 auto cb = [callback](V1_3::ErrorStatus errorStatus,
229 std::vector<OutputShape> outputShapes,
230 const Timing& timing,
231 std::string callingFunction)
232 {
233 NotifyCallbackAndCheck(callback, errorStatus, outputShapes, timing, callingFunction);
234 };
235
236 return convertToV1_0(Execute(convertToV1_3(request), measureTiming, cb));
237}
238
239template<typename HalVersion>
240Return <V1_3::ErrorStatus> ArmnnPreparedModel_1_3<HalVersion>::execute_1_3(
241 const V1_3::Request& request,
242 MeasureTiming measureTiming,
243 const V1_3::OptionalTimePoint&,
Kevin May352d8382020-03-31 15:03:42 +0100244 const V1_3::OptionalTimeoutDuration&,
Kevin May42477c12020-03-26 13:34:14 +0000245 const sp<V1_3::IExecutionCallback>& callback)
246{
247 if (callback.get() == nullptr)
248 {
249 ALOGE("ArmnnPreparedModel_1_3::execute_1_3 invalid callback passed");
250 return V1_3::ErrorStatus::INVALID_ARGUMENT;
251 }
252
253 auto cb = [callback](V1_3::ErrorStatus errorStatus,
254 std::vector<OutputShape> outputShapes,
255 const Timing& timing,
256 std::string callingFunction)
257 {
258 NotifyCallbackAndCheck(callback, errorStatus, outputShapes, timing, callingFunction);
259 };
260
261 return Execute(request, measureTiming, cb);
262}
263
Sadik Armagand7be72e2020-04-23 12:56:05 +0100264/// This class is inspired by the sample implementation in Android named SampleFencedExecutionCallback.
265/// The original code is licensed under Apache-2.0 and can be found at the following link:
266/// https://android.googlesource.com/platform/frameworks/ml/+/master/nn/driver/sample/SampleDriver.h
267class ArmnnFencedExecutionCallback : public V1_3::IFencedExecutionCallback
268{
269public:
270 ArmnnFencedExecutionCallback(V1_3::ErrorStatus errorStatus, Timing timing, Timing fenceTiming)
271 : m_ErrorStatus(errorStatus), m_Timing(timing), m_FenceTiming(fenceTiming) {}
272 ~ArmnnFencedExecutionCallback() {}
273
274 Return<void> getExecutionInfo(getExecutionInfo_cb callback) override
275 {
276 callback(m_ErrorStatus, m_Timing, m_FenceTiming);
277 return Void();
278 }
279private:
280 V1_3::ErrorStatus m_ErrorStatus;
281 Timing m_Timing;
282 Timing m_FenceTiming;
283};
284
Kevin May42477c12020-03-26 13:34:14 +0000285template<typename HalVersion>
Sadik Armagand7be72e2020-04-23 12:56:05 +0100286Return<void> ArmnnPreparedModel_1_3<HalVersion>::executeFenced(const V1_3::Request& request,
287 const hidl_vec<hidl_handle>& fenceWaitFor,
288 MeasureTiming measureTiming,
Sadik Armagan7b9ce8d2020-04-21 10:39:28 +0100289 const OptionalTimePoint& deadline,
290 const OptionalTimeoutDuration& loopTimeoutDuration,
Kevin May352d8382020-03-31 15:03:42 +0100291 const OptionalTimeoutDuration&,
Kevin May42477c12020-03-26 13:34:14 +0000292 executeFenced_cb cb)
293{
Sadik Armagan7b9ce8d2020-04-21 10:39:28 +0100294 ALOGV("ArmnnPreparedModel_1_3::executeFenced(...)");
295 if (cb == nullptr)
296 {
297 ALOGE("ArmnnPreparedModel_1_3::executeFenced invalid callback passed");
298 cb(ErrorStatus::INVALID_ARGUMENT, hidl_handle(nullptr), nullptr);
299 return Void();
300 }
301
302 if (deadline.getDiscriminator() != OptionalTimePoint::hidl_discriminator::none)
303 {
304 ALOGW("ArmnnPreparedModel_1_3::executeFenced parameter deadline is set but not supported.");
305 }
306
307 if (loopTimeoutDuration.getDiscriminator() != OptionalTimeoutDuration::hidl_discriminator::none)
308 {
309 ALOGW("ArmnnPreparedModel_1_3::executeFenced parameter loopTimeoutDuration is set but not supported.");
310 }
311
Finn Williamsa4983ce2020-07-23 12:55:12 +0100312 if (!android::nn::validateRequest(request, m_Model, /*allowUnspecifiedOutput=*/false))
313 {
314 ALOGV("ArmnnPreparedModel_1_3::executeFenced outputs must be specified for fenced execution ");
315 cb(ErrorStatus::INVALID_ARGUMENT, hidl_handle(nullptr), nullptr);
316 return Void();
317 }
318
Sadik Armagand7be72e2020-04-23 12:56:05 +0100319 ExecutionContext_1_3 ctx;
320 if (measureTiming == MeasureTiming::YES)
321 {
322 ctx.measureTimings = measureTiming;
323 ctx.driverStart = Now();
324 }
325
326 ALOGV("ArmnnPreparedModel_1_3::executeFenced(): %s", GetModelSummary(m_Model).c_str());
327 m_RequestCount++;
328
Sadik Armagand7be72e2020-04-23 12:56:05 +0100329 if (!m_RequestInputsAndOutputsDumpDir.empty())
330 {
331 ALOGD("Dumping inputs and outputs for request %" PRIuPTR, reinterpret_cast<std::uintptr_t>(&cb));
332 }
333
334 // This code snippet is inspired by the sample implementation in Android named SampleDriver::executeFenced()
335 // function. The original code is licensed under Apache-2.0 and can be found at the following link:
336 // https://android.googlesource.com/platform/frameworks/ml/+/master/nn/driver/sample/SampleDriver.cpp
337 const auto fenceSize = fenceWaitFor.size();
338 for (unsigned int index = 0; index < fenceSize; ++index)
339 {
340 auto fenceNativeHandle = fenceWaitFor[index].getNativeHandle();
341 if (!fenceNativeHandle)
342 {
343 cb(ErrorStatus::INVALID_ARGUMENT, hidl_handle(nullptr), nullptr);
344 return Void();
345 }
346
347 if (sync_wait(fenceNativeHandle->data[0], -1) < 0)
348 {
349 ALOGE("ArmnnPreparedModel_1_3::executeFenced sync fence failed.");
350 cb(ErrorStatus::GENERAL_FAILURE, hidl_handle(nullptr), nullptr);
351 return Void();
352 }
353 }
354
355 TimePoint fenceExecutionStart;
356 if (measureTiming == MeasureTiming::YES)
357 {
358 fenceExecutionStart = Now();
359 }
360
361 // map the memory pool into shared pointers
362 // use a shared memory pools vector on the heap, as it is passed to the request thread
363 auto memPools = std::make_shared<std::vector<android::nn::RunTimePoolInfo>>();
364
365 // allocate the tensors on the heap, as they are passed to the request thread
366 auto inputs = std::make_shared<armnn::InputTensors>();
367 auto outputs = std::make_shared<armnn::OutputTensors>();
368
369 auto [status, outShapes, timings, message] = PrepareMemoryForIO(*inputs, *outputs, *memPools, request);
370 if (status != V1_3::ErrorStatus::NONE)
371 {
372 cb(ErrorStatus::INVALID_ARGUMENT, hidl_handle(nullptr), nullptr);
373 return Void();
374 }
375
376 ALOGV("ArmnnPreparedModel_1_3::executeFenced(...) before ExecuteGraph");
377
378 // call it with nullCallback for now as we will report the error status from here..
379 auto nullCallback = [](V1_3::ErrorStatus, std::vector<OutputShape>, const Timing&, std::string) {};
380 CallbackContext_1_3 cbCtx;
381 cbCtx.callback = nullCallback;
382 cbCtx.ctx = ctx;
383
384 auto errorStatus = ExecuteGraph(memPools, *inputs, *outputs, cbCtx);
385 if (errorStatus != V1_3::ErrorStatus::NONE)
386 {
387 cb(errorStatus, hidl_handle(nullptr), nullptr);
388 return Void();
389 }
390 ALOGV("ArmnnPreparedModel_1_3::executeFenced(...) after ExecuteGraph");
391
392 Timing timing = g_NoTiming;
393 Timing fenceTiming = g_NoTiming;
394 if (measureTiming == MeasureTiming::YES)
395 {
Sadik Armagand7be72e2020-04-23 12:56:05 +0100396 fenceTiming.timeOnDevice = MicrosecondsDuration(ctx.deviceEnd, ctx.deviceStart);
Kevin May949a69e2020-04-24 10:21:40 +0100397 fenceTiming.timeInDriver = MicrosecondsDuration(ctx.driverEnd, fenceExecutionStart);
398 ALOGV("ArmnnPreparedModel_1_3::fenceFinishExecutionTiming - Device = %lu Driver = %lu",
Sadik Armagand7be72e2020-04-23 12:56:05 +0100399 fenceTiming.timeOnDevice, fenceTiming.timeInDriver);
400 }
401
402 sp<ArmnnFencedExecutionCallback> armnnFencedExecutionCallback =
403 new ArmnnFencedExecutionCallback(ErrorStatus::NONE, timing, fenceTiming);
404 cb(ErrorStatus::NONE, hidl_handle(nullptr), armnnFencedExecutionCallback);
Kevin May42477c12020-03-26 13:34:14 +0000405 return Void();
406}
407
408template<typename HalVersion>
409Return<V1_3::ErrorStatus> ArmnnPreparedModel_1_3<HalVersion>::PrepareMemoryForInputs(
410 armnn::InputTensors& inputs,
411 const V1_3::Request& request,
412 const std::vector<android::nn::RunTimePoolInfo>& memPools)
413{
414 inputs.reserve(request.inputs.size());
415 for (unsigned int i = 0; i < request.inputs.size(); i++)
416 {
417 const auto& inputArg = request.inputs[i];
418
419 const armnn::TensorInfo inputTensorInfo = m_Runtime->GetInputTensorInfo(m_NetworkId, i);
420 const armnn::Tensor inputTensor = GetTensorForRequestArgument(inputArg, inputTensorInfo, memPools);
421
422 if (inputTensor.GetMemoryArea() == nullptr)
423 {
424 ALOGE("Cannot execute request. Error converting request input %u to tensor", i);
425 return V1_3::ErrorStatus::GENERAL_FAILURE;
426 }
427
428 inputs.emplace_back(i, inputTensor);
429 }
430
431 return V1_3::ErrorStatus::NONE;
432}
433
434template<typename HalVersion>
435Return<V1_3::ErrorStatus> ArmnnPreparedModel_1_3<HalVersion>::PrepareMemoryForOutputs(
436 armnn::OutputTensors& outputs,
437 std::vector<OutputShape> &outputShapes,
438 const V1_3::Request& request,
439 const std::vector<android::nn::RunTimePoolInfo>& memPools)
440{
441 outputs.reserve(request.outputs.size());
442 for (unsigned int i = 0; i < request.outputs.size(); i++)
443 {
444 const auto& outputArg = request.outputs[i];
445
Finn Williamsa4983ce2020-07-23 12:55:12 +0100446 armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkId, i);
Kevin May42477c12020-03-26 13:34:14 +0000447 const armnn::Tensor outputTensor = GetTensorForRequestArgument(outputArg, outputTensorInfo, memPools);
448 if (outputTensor.GetMemoryArea() == nullptr)
449 {
450 ALOGE("Cannot execute request. Error converting request output %u to tensor", i);
451 return V1_3::ErrorStatus::GENERAL_FAILURE;
452 }
453
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100454 const size_t outputSize = outputTensorInfo.GetNumBytes();
455
Finn Williamsa4983ce2020-07-23 12:55:12 +0100456 unsigned int count = 0;
457 std::for_each(outputArg.dimensions.begin(), outputArg.dimensions.end(), [&](auto dim)
458 {
459 if (dim != 0)
460 {
461 outputTensorInfo.GetShape()[count] = dim;
462 }
463 else
464 {
465 outputTensorInfo.GetShape()[count] = outputArg.dimensions.size();
466 }
467
468 count++;
469 });
470
Finn Williamsa4983ce2020-07-23 12:55:12 +0100471 outputs.emplace_back(i, outputTensor);
472 outputShapes[i] = ComputeShape(outputTensorInfo);
473
474 if (outputArg.location.length < outputSize)
475 {
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100476 ALOGW("ArmnnPreparedModel_1_3::Execute failed outputArg.location.length (%s) < outputSize (%s)",
477 std::to_string(outputArg.location.length).c_str(), std::to_string(outputSize).c_str());
Finn Williamsa4983ce2020-07-23 12:55:12 +0100478 outputShapes[i].isSufficient = false;
479 return V1_3::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
480 }
481
Kevin May42477c12020-03-26 13:34:14 +0000482 const size_t bufferSize = memPools.at(outputArg.location.poolIndex).getHidlMemory().size();
483 if (bufferSize < outputSize)
484 {
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100485 ALOGW("ArmnnPreparedModel_1_3::Execute failed bufferSize (%s) < outputSize (%s)",
486 std::to_string(bufferSize).c_str(), std::to_string(outputSize).c_str());
Finn Williamsa4983ce2020-07-23 12:55:12 +0100487 outputShapes[i].isSufficient = false;
Kevin May42477c12020-03-26 13:34:14 +0000488 return V1_3::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
489 }
Kevin May42477c12020-03-26 13:34:14 +0000490 }
491
492 return V1_3::ErrorStatus::NONE;
493}
494
495template<typename HalVersion>
496std::tuple<V1_3::ErrorStatus, hidl_vec<OutputShape>, Timing, std::string>
497 ArmnnPreparedModel_1_3<HalVersion>::PrepareMemoryForIO(armnn::InputTensors& inputs,
498 armnn::OutputTensors& outputs,
499 std::vector<android::nn::RunTimePoolInfo>& memPools,
500 const V1_3::Request& request)
501{
502 if (!setRunTimePoolInfosFromMemoryPools(&memPools, request.pools))
503 {
Sadik Armaganef8a3932020-04-09 17:21:50 +0100504 return {ErrorStatus::INVALID_ARGUMENT, {}, g_NoTiming, "ArmnnPreparedModel_1_3::execute"};
Kevin May42477c12020-03-26 13:34:14 +0000505 }
506
507 // add the inputs and outputs with their data
508 try
509 {
510 if (PrepareMemoryForInputs(inputs, request, memPools) != V1_3::ErrorStatus::NONE)
511 {
512 return {ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_3::execute"};
513 }
514
515 std::vector<OutputShape> outputShapes(request.outputs.size());
516
517 auto errorStatus = PrepareMemoryForOutputs(outputs, outputShapes, request, memPools);
518 if (errorStatus != V1_3::ErrorStatus::NONE)
519 {
520 return {errorStatus, outputShapes, g_NoTiming, "ArmnnPreparedModel_1_3::execute"};
521 }
522 }
523 catch (armnn::Exception& e)
524 {
525 ALOGW("armnn::Exception caught while preparing for EnqueueWorkload: %s", e.what());
526 return {ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_3::execute"};
527 }
528 catch (std::exception& e)
529 {
530 ALOGE("std::exception caught while preparing for EnqueueWorkload: %s", e.what());
531 return {ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_3::execute"};
532 }
533
534 return {V1_3::ErrorStatus::NONE, {}, g_NoTiming, "ArmnnPreparedModel_1_3::execute"};
535}
536
537template<typename HalVersion>
538template<typename CallbackContext>
539Return<void> ArmnnPreparedModel_1_3<HalVersion>::ExecuteSynchronously(const V1_3::Request& request,
540 CallbackContext cbCtx)
541{
542 if (cbCtx.ctx.measureTimings == MeasureTiming::YES)
543 {
544 cbCtx.ctx.driverStart = Now();
545 }
546
547 if (!android::nn::validateRequest(convertToV1_3(request), m_Model))
548 {
549 ALOGE("ArmnnPreparedModel_1_3::ExecuteSynchronously invalid request model");
550 cbCtx.callback(V1_3::ErrorStatus::INVALID_ARGUMENT,
551 {},
552 g_NoTiming,
553 "ArmnnPreparedModel_1_3::ExecuteSynchronously invalid request model");
554 return Void();
555 }
556
557 if (!android::nn::validateRequest(request, m_Model))
558 {
559 ALOGE("ArmnnPreparedModel_1_3::ExecuteSynchronously invalid request model");
560 cbCtx.callback(V1_3::ErrorStatus::INVALID_ARGUMENT,
561 {},
562 g_NoTiming,
563 "ArmnnPreparedModel_1_3::ExecuteSynchronously invalid request model");
Sadik Armaganef8a3932020-04-09 17:21:50 +0100564 return Void();
Kevin May42477c12020-03-26 13:34:14 +0000565 }
566
567
568 // map the memory pool into shared pointers
569 // use a shared memory pools vector on the heap, as it is passed to the request thread
570 auto memPools = std::make_shared<std::vector<android::nn::RunTimePoolInfo>>();
571
572 // allocate the tensors on the heap, as they are passed to the request thread
573 auto inputs = std::make_shared<armnn::InputTensors>();
574 auto outputs = std::make_shared<armnn::OutputTensors>();
575
576 auto [status, outputShapes, timing, message] = PrepareMemoryForIO(*inputs, *outputs, *memPools, request);
577 if (status != V1_3::ErrorStatus::NONE)
578 {
579 cbCtx.callback(status, outputShapes, timing, message);
Sadik Armaganef8a3932020-04-09 17:21:50 +0100580 return Void();
Kevin May42477c12020-03-26 13:34:14 +0000581 }
582
583 ALOGV("ArmnnPreparedModel_1_3::ExecuteSynchronously() before Execution");
584
585 ExecuteGraph(memPools, *inputs, *outputs, cbCtx);
586 return Void();
587}
588
589template<typename HalVersion>
590Return<void> ArmnnPreparedModel_1_3<HalVersion>::executeSynchronously(const V1_0::Request& request,
591 MeasureTiming measureTiming,
592 executeSynchronously_cb cb)
593{
594 ALOGV("ArmnnPreparedModel_1_3::executeSynchronously(): %s", GetModelSummary(m_Model).c_str());
595 m_RequestCount++;
596
597 if (cb == nullptr)
598 {
599 ALOGE("ArmnnPreparedModel_1_3::executeSynchronously invalid callback passed");
600 return Void();
601 }
602
603 auto cbWrapper = [cb](V1_3::ErrorStatus errorStatus,
604 std::vector<OutputShape> outputShapes,
605 const Timing& timing,
606 std::string)
607 {
608 cb(convertToV1_0(errorStatus), outputShapes, timing);
609 };
610
611 CallbackContext_1_3 cbCtx;
612 cbCtx.callback = cbWrapper;
613 cbCtx.ctx.measureTimings = measureTiming;
614
615 ExecuteSynchronously(convertToV1_3(request), cbCtx);
616 return Void();
617}
618
619template<typename HalVersion>
Kevin May352d8382020-03-31 15:03:42 +0100620Return<void> ArmnnPreparedModel_1_3<HalVersion>::executeSynchronously_1_3(
621 const V1_3::Request& request,
622 MeasureTiming measureTiming,
623 const V1_3::OptionalTimePoint& deadline,
624 const V1_3::OptionalTimeoutDuration& loopTimeoutDuration,
625 executeSynchronously_1_3_cb cb)
Kevin May42477c12020-03-26 13:34:14 +0000626{
627 ALOGV("ArmnnPreparedModel_1_3::executeSynchronously_1_3(): %s", GetModelSummary(m_Model).c_str());
628 m_RequestCount++;
629
630 if (cb == nullptr)
631 {
632 ALOGE("ArmnnPreparedModel_1_3::executeSynchronously_1_3 invalid callback passed");
633 return Void();
634 }
635
636 if (deadline.getDiscriminator() != OptionalTimePoint::hidl_discriminator::none)
637 {
Sadik Armagan7b9ce8d2020-04-21 10:39:28 +0100638 ALOGW("ArmnnPreparedModel_1_3::executeSynchronously_1_3 parameter deadline is set but not supported.");
Kevin May42477c12020-03-26 13:34:14 +0000639 }
640
Kevin May352d8382020-03-31 15:03:42 +0100641 if (loopTimeoutDuration.getDiscriminator() != OptionalTimeoutDuration::hidl_discriminator::none)
Sadik Armagan7b9ce8d2020-04-21 10:39:28 +0100642 {
643 ALOGW(
644 "ArmnnPreparedModel_1_3::executeSynchronously_1_3 parameter loopTimeoutDuration is set but not supported.");
Kevin May352d8382020-03-31 15:03:42 +0100645 }
646
Kevin May42477c12020-03-26 13:34:14 +0000647 auto cbWrapper = [cb](V1_3::ErrorStatus errorStatus,
648 std::vector<OutputShape> outputShapes,
649 const Timing& timing,
650 std::string)
651 {
652 cb(errorStatus, outputShapes, timing);
653 };
654
655 CallbackContext_1_3 cbCtx;
656 cbCtx.callback = cbWrapper;
657 cbCtx.ctx.measureTimings = measureTiming;
658
659 ExecuteSynchronously(request, cbCtx);
660 return Void();
661}
662
663template<typename HalVersion>
664Return<void> ArmnnPreparedModel_1_3<HalVersion>::configureExecutionBurst(
665 const sp<V1_2::IBurstCallback>& callback,
666 const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
667 const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
668 V1_3::IPreparedModel::configureExecutionBurst_cb cb)
669{
670 ALOGV("ArmnnPreparedModel_1_3::configureExecutionBurst");
671 const sp<V1_2::IBurstContext> burst = ExecutionBurstServer::create(callback,
672 requestChannel,
673 resultChannel,
674 this);
675
676 if (burst == nullptr)
677 {
678 cb(V1_0::ErrorStatus::GENERAL_FAILURE, {});
679 }
680 else
681 {
682 cb(V1_0::ErrorStatus::NONE, burst);
683 }
684 return Void();
685}
686
687template<typename HalVersion>
688template<typename CallbackContext>
Sadik Armagand7be72e2020-04-23 12:56:05 +0100689Return <V1_3::ErrorStatus> ArmnnPreparedModel_1_3<HalVersion>::ExecuteGraph(
Kevin May42477c12020-03-26 13:34:14 +0000690 std::shared_ptr<std::vector<::android::nn::RunTimePoolInfo>>& pMemPools,
691 armnn::InputTensors& inputTensors,
692 armnn::OutputTensors& outputTensors,
693 CallbackContext cb)
694{
695 ALOGV("ArmnnPreparedModel_1_3::ExecuteGraph(...)");
696
Kevin May42477c12020-03-26 13:34:14 +0000697 DumpTensorsIfRequired("Input", inputTensors);
698
699 std::vector<OutputShape> outputShapes(outputTensors.size());
700 for (unsigned int i = 0; i < outputTensors.size(); i++)
701 {
702 std::pair<int, armnn::Tensor> outputTensorPair = outputTensors[i];
703 const armnn::Tensor outputTensor = outputTensorPair.second;
704 const armnn::TensorInfo outputTensorInfo = outputTensor.GetInfo();
705
706 outputShapes[i] = ComputeShape(outputTensorInfo);
707 }
708
709 // run it
710 try
711 {
712 if (cb.ctx.measureTimings == MeasureTiming::YES)
713 {
Sadik Armagand7be72e2020-04-23 12:56:05 +0100714 cb.ctx.deviceStart = Now();
Kevin May42477c12020-03-26 13:34:14 +0000715 }
716
717 armnn::Status status = m_Runtime->EnqueueWorkload(m_NetworkId, inputTensors, outputTensors);
718
719 if (cb.ctx.measureTimings == MeasureTiming::YES)
720 {
Sadik Armagand7be72e2020-04-23 12:56:05 +0100721 cb.ctx.deviceEnd = Now();
Kevin May42477c12020-03-26 13:34:14 +0000722 }
723 if (status != armnn::Status::Success)
724 {
725 ALOGW("EnqueueWorkload failed");
Sadik Armagand7be72e2020-04-23 12:56:05 +0100726 cb.callback(V1_3::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_3::ExecuteGraph");
727 return V1_3::ErrorStatus::GENERAL_FAILURE;
Kevin May42477c12020-03-26 13:34:14 +0000728 }
729 }
730 catch (armnn::Exception& e)
731 {
732 ALOGW("armnn:Exception caught from EnqueueWorkload: %s", e.what());
733 cb.callback(V1_3::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_3::ExecuteGraph");
Sadik Armagand7be72e2020-04-23 12:56:05 +0100734 return V1_3::ErrorStatus::GENERAL_FAILURE;
Kevin May42477c12020-03-26 13:34:14 +0000735 }
736 catch (std::exception& e)
737 {
738 ALOGE("std::exception caught from EnqueueWorkload: %s", e.what());
739 cb.callback(V1_3::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_3::ExecuteGraph");
Sadik Armagand7be72e2020-04-23 12:56:05 +0100740 return V1_3::ErrorStatus::GENERAL_FAILURE;
Kevin May42477c12020-03-26 13:34:14 +0000741 }
742
743 CommitPools(*pMemPools);
744
745 DumpTensorsIfRequired("Output", outputTensors);
746
747 if (cb.ctx.measureTimings == MeasureTiming::YES)
748 {
Kevin May949a69e2020-04-24 10:21:40 +0100749 cb.ctx.driverEnd = Now();
Kevin May42477c12020-03-26 13:34:14 +0000750 Timing timing;
Sadik Armagand7be72e2020-04-23 12:56:05 +0100751 timing.timeOnDevice = MicrosecondsDuration(cb.ctx.deviceEnd, cb.ctx.deviceStart);
Kevin May949a69e2020-04-24 10:21:40 +0100752 timing.timeInDriver = MicrosecondsDuration(cb.ctx.driverEnd, cb.ctx.driverStart);
753 ALOGV("ArmnnPreparedModel_1_3::execute timing - Device = %lu Driver = %lu", timing.timeOnDevice,
Kevin May42477c12020-03-26 13:34:14 +0000754 timing.timeInDriver);
755 cb.callback(V1_3::ErrorStatus::NONE, outputShapes, timing, "ArmnnPreparedModel_1_3::ExecuteGraph");
Sadik Armagand7be72e2020-04-23 12:56:05 +0100756 } else
757 {
Kevin May42477c12020-03-26 13:34:14 +0000758 cb.callback(V1_3::ErrorStatus::NONE, outputShapes, g_NoTiming, "ArmnnPreparedModel_1_3::ExecuteGraph");
759 }
Sadik Armagand7be72e2020-04-23 12:56:05 +0100760 return V1_3::ErrorStatus::NONE;
Kevin May42477c12020-03-26 13:34:14 +0000761}
762
763template<typename HalVersion>
764bool ArmnnPreparedModel_1_3<HalVersion>::ExecuteWithDummyInputs()
765{
766 std::vector<std::vector<char>> storage;
767 armnn::InputTensors inputTensors;
768 for (unsigned int i = 0; i < getMainModel(m_Model).inputIndexes.size(); i++)
769 {
770 const armnn::TensorInfo inputTensorInfo = m_Runtime->GetInputTensorInfo(m_NetworkId, i);
771 storage.emplace_back(inputTensorInfo.GetNumBytes());
772 const armnn::ConstTensor inputTensor(inputTensorInfo, storage.back().data());
773
774 inputTensors.emplace_back(i, inputTensor);
775 }
776
777 armnn::OutputTensors outputTensors;
778 for (unsigned int i = 0; i < getMainModel(m_Model).outputIndexes.size(); i++)
779 {
780 const armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkId, i);
781 storage.emplace_back(outputTensorInfo.GetNumBytes());
782 const armnn::Tensor outputTensor(outputTensorInfo, storage.back().data());
783
784 outputTensors.emplace_back(i, outputTensor);
785 }
786
787 auto nullCallback = [](V1_3::ErrorStatus, std::vector<OutputShape>, const Timing&, std::string) {};
788 CallbackContext_1_3 callbackContext;
789 callbackContext.callback = nullCallback;
790 callbackContext.ctx.measureTimings = MeasureTiming::NO;
791 auto memPools = std::make_shared<std::vector<::android::nn::RunTimePoolInfo>>();
Sadik Armagand7be72e2020-04-23 12:56:05 +0100792
793 auto errorStatus = ExecuteGraph(memPools,
794 inputTensors,
795 outputTensors,
796 callbackContext);
797 return errorStatus == V1_3::ErrorStatus::NONE;
Kevin May42477c12020-03-26 13:34:14 +0000798}
799
800template<typename HalVersion>
801Return <V1_3::ErrorStatus> ArmnnPreparedModel_1_3<HalVersion>::Execute(const V1_3::Request& request,
802 MeasureTiming measureTiming,
803 CallbackAsync_1_3 callback)
804{
805 ExecutionContext_1_3 ctx;
806 if (measureTiming == MeasureTiming::YES)
807 {
808 ctx.measureTimings = measureTiming;
809 ctx.driverStart = Now();
810 }
811
812 ALOGV("ArmnnPreparedModel_1_3::execute(): %s", GetModelSummary(m_Model).c_str());
813 m_RequestCount++;
814
815 if (!android::nn::validateRequest(request, m_Model))
816 {
817 callback(V1_3::ErrorStatus::INVALID_ARGUMENT, {}, g_NoTiming, "ArmnnPreparedModel_1_3::execute");
818 return V1_3::ErrorStatus::INVALID_ARGUMENT;
819 }
820
821 if (!m_RequestInputsAndOutputsDumpDir.empty())
822 {
823 ALOGD("Dumping inputs and outputs for request %" PRIuPTR, reinterpret_cast<std::uintptr_t>(&callback));
824 }
825
826 // map the memory pool into shared pointers
827 // use a shared memory pools vector on the heap, as it is passed to the request thread
828 auto memPools = std::make_shared<std::vector<android::nn::RunTimePoolInfo>>();
829
830 // allocate the tensors on the heap, as they are passed to the request thread
831 auto inputTensors = std::make_shared<armnn::InputTensors>();
832 auto outputTensors = std::make_shared<armnn::OutputTensors>();
833
834 auto [status, outShapes, timing, message] = PrepareMemoryForIO(*inputTensors, *outputTensors,
835 *memPools, request);
836 if (status != V1_3::ErrorStatus::NONE)
837 {
838 callback(status, outShapes, timing, message);
839 }
840
841 switch(status)
842 {
843 case V1_3::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE:
844 return V1_3::ErrorStatus::NONE;
845 case V1_3::ErrorStatus::GENERAL_FAILURE:
846 return V1_3::ErrorStatus::GENERAL_FAILURE;
847 default:
848 {}
849 }
850
851 ALOGV("ArmnnPreparedModel_1_3::execute(...) before PostMsg");
852
853 // post the request for asynchronous execution
854 CallbackContext_1_3 cb;
855 cb.callback = callback;
856 cb.ctx = ctx;
857 m_RequestThread.PostMsg(this, memPools, inputTensors, outputTensors, cb);
858 ALOGV("ArmnnPreparedModel_1_3::execute(...) after PostMsg");
859 return V1_3::ErrorStatus::NONE;
860}
861
Narumol Prangnawaratcad4e912020-06-02 12:07:43 +0100862template<typename HalVersion>
863V1_3::Priority ArmnnPreparedModel_1_3<HalVersion>::GetModelPriority()
864{
865 return m_ModelPriority;
866}
867
Kevin May42477c12020-03-26 13:34:14 +0000868#ifdef ARMNN_ANDROID_NN_V1_3
869template class ArmnnPreparedModel_1_3<hal_1_3::HalPolicy>;
Sadik Armagand7be72e2020-04-23 12:56:05 +0100870template Return <V1_3::ErrorStatus> ArmnnPreparedModel_1_3<hal_1_3::HalPolicy>::ExecuteGraph<CallbackContext_1_3>(
Kevin May42477c12020-03-26 13:34:14 +0000871 std::shared_ptr<std::vector<::android::nn::RunTimePoolInfo>>& pMemPools,
872 armnn::InputTensors& pInputTensors,
873 armnn::OutputTensors& pOutputTensors,
874 CallbackContext_1_3 cb);
875#endif
876
877} // namespace armnn_driver