blob: 84ff6e243ae3bc77a636081645996efff639b34f [file] [log] [blame]
Mike Kellyb5fdf382019-06-11 16:35:25 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
Jan Eilers43a430d2020-02-28 15:40:44 +00005// Note: the ArmnnBurstExecutorWithCache in this file is based on Android code
6// under the Apache 2.0 license. See comment below for details.
7//
Mike Kellyb5fdf382019-06-11 16:35:25 +01008
9#define LOG_TAG "ArmnnDriver"
10
11#include "ArmnnPreparedModel_1_2.hpp"
12#include "Utils.hpp"
13
14#include <boost/format.hpp>
15#include <log/log.h>
16#include <OperationsUtils.h>
17#include <ExecutionBurstServer.h>
18#include <ValidateHal.h>
19
20#include <cassert>
21#include <cinttypes>
22
23using namespace android;
24using namespace android::hardware;
25
Mike Kellyb5fdf382019-06-11 16:35:25 +010026namespace {
27
Mike Kelly44381512019-07-08 17:37:35 +010028static const Timing g_NoTiming = {.timeOnDevice = UINT64_MAX, .timeInDriver = UINT64_MAX};
Mike Kellyb5fdf382019-06-11 16:35:25 +010029using namespace armnn_driver;
Mike Kelly44381512019-07-08 17:37:35 +010030using TimePoint = std::chrono::steady_clock::time_point;
31
32TimePoint Now()
33{
34 return std::chrono::steady_clock::now();
35}
36
37unsigned long MicrosecondsDuration(TimePoint endPoint, TimePoint startPoint)
38{
39 return static_cast<unsigned long>(std::chrono::duration_cast<std::chrono::microseconds>(
40 endPoint - startPoint).count());
41}
Mike Kellyb5fdf382019-06-11 16:35:25 +010042
Mike Kelly65c42dc2019-07-22 14:06:00 +010043void NotifyCallbackAndCheck(const ::android::sp<V1_0::IExecutionCallback>& callback,
44 ErrorStatus errorStatus,
45 std::vector<OutputShape>,
46 const Timing,
Mike Kellyb5fdf382019-06-11 16:35:25 +010047 std::string callingFunction)
48{
49 Return<void> returned = callback->notify(errorStatus);
50 // This check is required, if the callback fails and it isn't checked it will bring down the service
51 if (!returned.isOk())
52 {
53 ALOGE("ArmnnDriver::%s: hidl callback failed to return properly: %s",
54 callingFunction.c_str(), returned.description().c_str());
55 }
56}
57
Mike Kelly65c42dc2019-07-22 14:06:00 +010058void NotifyCallbackAndCheck(const ::android::sp<V1_2::IExecutionCallback>& callback,
59 ErrorStatus errorStatus,
60 std::vector<OutputShape> outputShapes,
61 const Timing timing,
Mike Kellyb5fdf382019-06-11 16:35:25 +010062 std::string callingFunction)
63{
Mike Kelly65c42dc2019-07-22 14:06:00 +010064 Return<void> returned = callback->notify_1_2(errorStatus, outputShapes, timing);
Mike Kellyb5fdf382019-06-11 16:35:25 +010065 // This check is required, if the callback fails and it isn't checked it will bring down the service
66 if (!returned.isOk())
67 {
68 ALOGE("ArmnnDriver::%s: hidl callback failed to return properly: %s",
69 callingFunction.c_str(), returned.description().c_str());
70 }
71}
72
73bool ValidateRequestArgument(const RequestArgument& requestArg, const armnn::TensorInfo& tensorInfo)
74{
75 if (requestArg.dimensions.size() != 0)
76 {
77 if (requestArg.dimensions.size() != tensorInfo.GetNumDimensions())
78 {
79 ALOGE("Mismatched dimensions (request argument: %zu, expected: %u)",
80 requestArg.dimensions.size(), tensorInfo.GetNumDimensions());
81 return false;
82 }
83
84 for (unsigned int d = 0; d < tensorInfo.GetNumDimensions(); ++d)
85 {
86 if (requestArg.dimensions[d] != tensorInfo.GetShape()[d])
87 {
88 ALOGE("Mismatched size for dimension %d (request argument: %u, expected %u)",
89 d, requestArg.dimensions[d], tensorInfo.GetShape()[d]);
90 return false;
91 }
92 }
93 }
94
95 return true;
96}
97
98armnn::Tensor GetTensorForRequestArgument(const RequestArgument& requestArg,
99 const armnn::TensorInfo& tensorInfo,
100 const std::vector<::android::nn::RunTimePoolInfo>& requestPools)
101{
102 if (!ValidateRequestArgument(requestArg, tensorInfo))
103 {
104 return armnn::Tensor();
105 }
106
107 return armnn::Tensor(tensorInfo, GetMemoryFromPool(requestArg.location, requestPools));
108}
109
110inline std::string BuildTensorName(const char* tensorNamePrefix, std::size_t index)
111{
112 return tensorNamePrefix + std::to_string(index);
113}
114
115} // anonymous namespace
116
117using namespace android::hardware;
118
119namespace armnn_driver
120{
121
122template<typename HalVersion>
Mike Kelly65c42dc2019-07-22 14:06:00 +0100123RequestThread<ArmnnPreparedModel_1_2, HalVersion, ArmnnCallback_1_2>
124 ArmnnPreparedModel_1_2<HalVersion>::m_RequestThread;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100125
126template<typename HalVersion>
127template<typename TensorBindingCollection>
128void ArmnnPreparedModel_1_2<HalVersion>::DumpTensorsIfRequired(char const* tensorNamePrefix,
129 const TensorBindingCollection& tensorBindings)
130{
131 if (!m_RequestInputsAndOutputsDumpDir.empty())
132 {
133 const std::string requestName = boost::str(boost::format("%1%_%2%.dump") % m_NetworkId % m_RequestCount);
134 for (std::size_t i = 0u; i < tensorBindings.size(); ++i)
135 {
136 DumpTensor(m_RequestInputsAndOutputsDumpDir,
137 requestName,
138 BuildTensorName(tensorNamePrefix, i),
139 tensorBindings[i].second);
140 }
141 }
142}
143
144template<typename HalVersion>
145ArmnnPreparedModel_1_2<HalVersion>::ArmnnPreparedModel_1_2(armnn::NetworkId networkId,
146 armnn::IRuntime* runtime,
147 const V1_2::Model& model,
148 const std::string& requestInputsAndOutputsDumpDir,
149 const bool gpuProfilingEnabled)
150 : m_NetworkId(networkId)
151 , m_Runtime(runtime)
152 , m_Model(model)
153 , m_RequestCount(0)
154 , m_RequestInputsAndOutputsDumpDir(requestInputsAndOutputsDumpDir)
155 , m_GpuProfilingEnabled(gpuProfilingEnabled)
156{
157 // Enable profiling if required.
158 m_Runtime->GetProfiler(m_NetworkId)->EnableProfiling(m_GpuProfilingEnabled);
159}
160
161template<typename HalVersion>
162ArmnnPreparedModel_1_2<HalVersion>::~ArmnnPreparedModel_1_2()
163{
164 // Get a hold of the profiler used by this model.
165 std::shared_ptr<armnn::IProfiler> profiler = m_Runtime->GetProfiler(m_NetworkId);
166
167 // Unload the network associated with this model.
168 m_Runtime->UnloadNetwork(m_NetworkId);
169
170 // Dump the profiling info to a file if required.
171 DumpJsonProfilingIfRequired(m_GpuProfilingEnabled, m_RequestInputsAndOutputsDumpDir, m_NetworkId, profiler.get());
172}
173
174template<typename HalVersion>
175Return <ErrorStatus> ArmnnPreparedModel_1_2<HalVersion>::execute(const Request& request,
176 const ::android::sp<V1_0::IExecutionCallback>& callback)
177{
Mike Kelly65c42dc2019-07-22 14:06:00 +0100178 if (callback.get() == nullptr)
179 {
180 ALOGE("ArmnnPreparedModel_1_2::execute invalid callback passed");
181 return ErrorStatus::INVALID_ARGUMENT;
182 }
183
184 auto cb = [callback](ErrorStatus errorStatus,
185 std::vector<OutputShape> outputShapes,
186 const Timing& timing,
187 std::string callingFunction)
188 {
189 NotifyCallbackAndCheck(callback, errorStatus, outputShapes, timing, callingFunction);
190 };
191
192 return Execute(request, MeasureTiming::NO, cb);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100193}
194
195template<typename HalVersion>
196Return <ErrorStatus> ArmnnPreparedModel_1_2<HalVersion>::execute_1_2(const Request& request,
Mike Kelly65c42dc2019-07-22 14:06:00 +0100197 MeasureTiming measureTiming,
Mike Kellyb5fdf382019-06-11 16:35:25 +0100198 const sp<V1_2::IExecutionCallback>& callback)
199{
Mike Kelly65c42dc2019-07-22 14:06:00 +0100200 if (callback.get() == nullptr)
201 {
202 ALOGE("ArmnnPreparedModel_1_2::execute_1_2 invalid callback passed");
203 return ErrorStatus::INVALID_ARGUMENT;
204 }
205
206 auto cb = [callback](ErrorStatus errorStatus,
207 std::vector<OutputShape> outputShapes,
208 const Timing& timing,
209 std::string callingFunction)
210 {
211 NotifyCallbackAndCheck(callback, errorStatus, outputShapes, timing, callingFunction);
212 };
213
214 return Execute(request, measureTiming, cb);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100215}
216
217template<typename HalVersion>
218Return<void> ArmnnPreparedModel_1_2<HalVersion>::executeSynchronously(const Request& request,
Mike Kelly44381512019-07-08 17:37:35 +0100219 MeasureTiming measureTiming,
220 executeSynchronously_cb cb)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100221{
222 ALOGV("ArmnnPreparedModel_1_2::executeSynchronously(): %s", GetModelSummary(m_Model).c_str());
223 m_RequestCount++;
224
225 if (cb == nullptr)
226 {
227 ALOGE("ArmnnPreparedModel_1_2::executeSynchronously invalid callback passed");
228 return Void();
229 }
230
Mike Kelly44381512019-07-08 17:37:35 +0100231 TimePoint driverStart, driverEnd, deviceStart, deviceEnd;
232
233 if (measureTiming == MeasureTiming::YES)
234 {
235 driverStart = Now();
236 }
237
Mike Kellyb5fdf382019-06-11 16:35:25 +0100238 if (!android::nn::validateRequest(request, m_Model))
239 {
Mike Kelly44381512019-07-08 17:37:35 +0100240 ALOGE("ArmnnPreparedModel_1_2::executeSynchronously invalid request model");
Mike Kellyb5fdf382019-06-11 16:35:25 +0100241 cb(ErrorStatus::INVALID_ARGUMENT, {}, g_NoTiming);
242 return Void();
243 }
244
245 // allocate the tensors on the heap, as they are passed to the request thread
246 auto pInputTensors = std::make_shared<armnn::InputTensors>();
247 auto pOutputTensors = std::make_shared<armnn::OutputTensors>();
248
249 // map the memory pool into shared pointers
250 // use a shared memory pools vector on the heap, as it is passed to the request thread
251 auto pMemPools = std::make_shared<std::vector<android::nn::RunTimePoolInfo>>();
252
253 if (!setRunTimePoolInfosFromHidlMemories(pMemPools.get(), request.pools))
254 {
255 cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
256 return Void();
257 }
Mike Kelly65c42dc2019-07-22 14:06:00 +0100258 std::vector<OutputShape> outputShapes(request.outputs.size());
Mike Kellyb5fdf382019-06-11 16:35:25 +0100259
Mike Kellyb5fdf382019-06-11 16:35:25 +0100260 try
261 {
262 pInputTensors->reserve(request.inputs.size());
263 for (unsigned int i = 0; i < request.inputs.size(); i++)
264 {
265 const auto& inputArg = request.inputs[i];
266
267 const armnn::TensorInfo inputTensorInfo = m_Runtime->GetInputTensorInfo(m_NetworkId, i);
268 const armnn::Tensor inputTensor = GetTensorForRequestArgument(inputArg, inputTensorInfo, *pMemPools);
269
270 if (inputTensor.GetMemoryArea() == nullptr)
271 {
272 ALOGE("Cannot execute request. Error converting request input %u to tensor", i);
273 cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
274 return Void();
275 }
276
277 pInputTensors->emplace_back(i, inputTensor);
278 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100279 pOutputTensors->reserve(request.outputs.size());
Mike Kelly65c42dc2019-07-22 14:06:00 +0100280
Mike Kellyb5fdf382019-06-11 16:35:25 +0100281 for (unsigned int i = 0; i < request.outputs.size(); i++)
282 {
283 const auto& outputArg = request.outputs[i];
284
285 const armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkId, i);
286 const armnn::Tensor outputTensor = GetTensorForRequestArgument(outputArg, outputTensorInfo, *pMemPools);
287
288 if (outputTensor.GetMemoryArea() == nullptr)
289 {
290 ALOGE("Cannot execute request. Error converting request output %u to tensor", i);
291 cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
292 return Void();
293 }
Mike Kelly65c42dc2019-07-22 14:06:00 +0100294 const size_t outputSize = outputTensorInfo.GetNumBytes();
295 const size_t bufferSize = pMemPools->at(outputArg.location.poolIndex).getHidlMemory().size();
296
297 hidl_vec<uint32_t> dimensions;
298
299 armnn::TensorShape tensorShape = outputTensorInfo.GetShape();
300 const unsigned int numDims = tensorShape.GetNumDimensions();
301 dimensions.resize(numDims);
302
303 for (unsigned int outputIdx = 0u; outputIdx < numDims; ++outputIdx)
304 {
305 dimensions[outputIdx] = tensorShape[outputIdx];
306 }
307 outputShapes[i].dimensions = dimensions;
308 outputShapes[i].isSufficient = bufferSize >= outputSize;
309
310 if (bufferSize < outputSize)
311 {
312 ALOGW("ArmnnPreparedModel_1_2::Execute failed");
313 cb(ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, outputShapes, g_NoTiming);
314 return Void();
315 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100316
317 pOutputTensors->emplace_back(i, outputTensor);
318 }
319 }
Kevin May7bdaac52020-02-10 12:10:07 +0000320 catch (armnn::Exception& e)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100321 {
Kevin May7bdaac52020-02-10 12:10:07 +0000322 ALOGW("armnn::Exception caught while preparing for EnqueueWorkload: %s", e.what());
Mike Kellyb5fdf382019-06-11 16:35:25 +0100323 cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
324 return Void();
325 }
Kevin May7bdaac52020-02-10 12:10:07 +0000326 catch (std::exception& e)
327 {
328 ALOGE("std::exception caught while preparing for EnqueueWorkload: %s", e.what());
329 cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
330 return Void();
331 }
332
Mike Kellyb5fdf382019-06-11 16:35:25 +0100333 ALOGV("ArmnnPreparedModel_1_2::executeSynchronously() before Execution");
334
335 DumpTensorsIfRequired("Input", *pInputTensors);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100336 // run it
337 try
338 {
Mike Kelly44381512019-07-08 17:37:35 +0100339 if (measureTiming == MeasureTiming::YES)
340 {
341 deviceStart = Now();
342 }
343
Mike Kellyb5fdf382019-06-11 16:35:25 +0100344 armnn::Status status = m_Runtime->EnqueueWorkload(m_NetworkId, *pInputTensors, *pOutputTensors);
345
Mike Kelly44381512019-07-08 17:37:35 +0100346 if (measureTiming == MeasureTiming::YES)
347 {
348 deviceEnd = Now();
349 }
350
Mike Kellyb5fdf382019-06-11 16:35:25 +0100351 if (status != armnn::Status::Success)
352 {
353 ALOGW("EnqueueWorkload failed");
354 cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
355 return Void();
356 }
357 }
Kevin May7bdaac52020-02-10 12:10:07 +0000358 catch (armnn::Exception& e)
359 {
360 ALOGW("armnn::Exception caught from EnqueueWorkload: %s", e.what());
361 cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
362 return Void();
363 }
Derek Lambertib9cb8442019-11-28 13:34:48 +0000364 catch (std::exception& e)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100365 {
Kevin May7bdaac52020-02-10 12:10:07 +0000366 ALOGE("std::exception caught from EnqueueWorkload: %s", e.what());
Mike Kellyb5fdf382019-06-11 16:35:25 +0100367 cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
368 return Void();
369 }
370
371 DumpTensorsIfRequired("Output", *pOutputTensors);
372
373 // Commit output buffers.
374 // Note that we update *all* pools, even if they aren't actually used as outputs -
375 // this is simpler and is what the CpuExecutor does.
376 for (android::nn::RunTimePoolInfo& pool : *pMemPools)
377 {
378 pool.update();
379 }
380 ALOGV("ArmnnPreparedModel_1_2::executeSynchronously() after Execution");
Mike Kelly44381512019-07-08 17:37:35 +0100381
382 if (measureTiming == MeasureTiming::YES)
383 {
384 driverEnd = Now();
385 Timing timing;
386 timing.timeOnDevice = MicrosecondsDuration(deviceEnd, deviceStart);
387 timing.timeInDriver = MicrosecondsDuration(driverEnd, driverStart);
388 ALOGV("ArmnnPreparedModel_1_2::executeSynchronously timing Device = %lu Driver = %lu", timing.timeOnDevice,
389 timing.timeInDriver);
Mike Kelly65c42dc2019-07-22 14:06:00 +0100390 cb(ErrorStatus::NONE, outputShapes, timing);
Mike Kelly44381512019-07-08 17:37:35 +0100391 }
392 else
393 {
Mike Kelly65c42dc2019-07-22 14:06:00 +0100394 cb(ErrorStatus::NONE, outputShapes, g_NoTiming);
Mike Kelly44381512019-07-08 17:37:35 +0100395 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100396 return Void();
397}
398
Jan Eilers43a430d2020-02-28 15:40:44 +0000399/// This class is strongly inspired by the default implementation in Android named DefaultBurstExecutorWithCache.
400/// The original code is licensed under Apache-2.0 and can be found at the following link:
401/// https://android.googlesource.com/platform/frameworks/
402/// ml/+/refs/tags/android-10.0.0_r20/nn/common/ExecutionBurstServer.cpp
Mike Kelly65c42dc2019-07-22 14:06:00 +0100403class ArmnnBurstExecutorWithCache : public ExecutionBurstServer::IBurstExecutorWithCache {
404public:
405 ArmnnBurstExecutorWithCache(IPreparedModel* preparedModel)
406 : m_PreparedModel(preparedModel)
407 {}
408
409 bool isCacheEntryPresent(int32_t slot) const override
410 {
411 const auto it = m_MemoryCache.find(slot);
412 return (it != m_MemoryCache.end()) && it->second.valid();
413 }
414
415 void addCacheEntry(const hidl_memory& memory, int32_t slot) override
416 {
417 m_MemoryCache[slot] = memory;
418 }
419
420 void removeCacheEntry(int32_t slot) override
421 {
422 m_MemoryCache.erase(slot);
423 }
424
425 std::tuple<ErrorStatus, hidl_vec<OutputShape>, Timing> execute(
426 const Request& request, const std::vector<int32_t>& slots,
427 MeasureTiming measure) override
428 {
429 ALOGV("ArmnnPreparedModel_1_2::BurstExecutorWithCache::execute");
430 hidl_vec<hidl_memory> pools(slots.size());
431
432 std::transform(slots.begin(), slots.end(), pools.begin(), [this](int32_t slot)
433 {
434 return m_MemoryCache[slot];
435 });
436
437 Request fullRequest = request;
438 fullRequest.pools = std::move(pools);
439
440 // Setup Callback
441 ErrorStatus returnedStatus = ErrorStatus::GENERAL_FAILURE;
442 hidl_vec<OutputShape> returnedOutputShapes;
443 Timing returnedTiming;
444 auto cb = [&returnedStatus, &returnedOutputShapes, &returnedTiming](ErrorStatus status,
445 const hidl_vec<OutputShape>& outputShapes,
446 const Timing& timing)
447 {
448 returnedStatus = status;
449 returnedOutputShapes = outputShapes;
450 returnedTiming = timing;
451 };
452
453 // Execute
454 ALOGV("ArmnnPreparedModel_1_2::BurstExecutorWithCache executing");
455 const Return<void> ret = m_PreparedModel->executeSynchronously(fullRequest, measure, cb);
456
457 if (!ret.isOk() || returnedStatus != ErrorStatus::NONE)
458 {
459 ALOGE("ArmnnPreparedModel_1_2::BurstExecutorWithCache::error executing");
460 }
461 return std::make_tuple(returnedStatus, std::move(returnedOutputShapes), returnedTiming);
462 }
463
464private:
465 IPreparedModel* const m_PreparedModel;
466 std::map<int, hidl_memory> m_MemoryCache;
467};
468
469
Mike Kellyb5fdf382019-06-11 16:35:25 +0100470template<typename HalVersion>
471Return<void> ArmnnPreparedModel_1_2<HalVersion>::configureExecutionBurst(
472 const sp<V1_2::IBurstCallback>& callback,
473 const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
474 const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
475 V1_2::IPreparedModel::configureExecutionBurst_cb cb)
476{
477 ALOGV("ArmnnPreparedModel_1_2::configureExecutionBurst");
Mike Kelly65c42dc2019-07-22 14:06:00 +0100478 const std::shared_ptr<ArmnnBurstExecutorWithCache> executorWithCache =
479 std::make_shared<ArmnnBurstExecutorWithCache>(this);
480 const sp<V1_2::IBurstContext> burst = ExecutionBurstServer::create(callback,
481 requestChannel,
482 resultChannel,
483 executorWithCache);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100484
Mike Kelly44381512019-07-08 17:37:35 +0100485 if (burst == nullptr)
486 {
Mike Kellyb5fdf382019-06-11 16:35:25 +0100487 cb(ErrorStatus::GENERAL_FAILURE, {});
Mike Kelly44381512019-07-08 17:37:35 +0100488 }
489 else
490 {
Mike Kellyb5fdf382019-06-11 16:35:25 +0100491 cb(ErrorStatus::NONE, burst);
492 }
493 return Void();
494}
495
496template<typename HalVersion>
497void ArmnnPreparedModel_1_2<HalVersion>::ExecuteGraph(
498 std::shared_ptr<std::vector<::android::nn::RunTimePoolInfo>>& pMemPools,
499 std::shared_ptr<armnn::InputTensors>& pInputTensors,
500 std::shared_ptr<armnn::OutputTensors>& pOutputTensors,
Mike Kelly65c42dc2019-07-22 14:06:00 +0100501 ArmnnCallback_1_2 cb)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100502{
503 ALOGV("ArmnnPreparedModel_1_2::ExecuteGraph(...)");
504
Mike Kelly65c42dc2019-07-22 14:06:00 +0100505 TimePoint driverEnd, deviceStart, deviceEnd;
506
Mike Kellyb5fdf382019-06-11 16:35:25 +0100507 DumpTensorsIfRequired("Input", *pInputTensors);
508
Mike Kelly65c42dc2019-07-22 14:06:00 +0100509 std::vector<std::pair<int, armnn::Tensor> > outputTensors = *pOutputTensors.get();
510 std::vector<OutputShape> outputShapes(outputTensors.size());
511
512 for (unsigned int i = 0; i < outputTensors.size(); i++)
513 {
514 std::pair<int, armnn::Tensor> outputTensorPair = outputTensors[i];
515 const armnn::Tensor outputTensor = outputTensorPair.second;
516 const armnn::TensorInfo outputTensorInfo = outputTensor.GetInfo();
517
518 hidl_vec<uint32_t> dimensions;
519
520 armnn::TensorShape tensorShape = outputTensorInfo.GetShape();
521 const unsigned int numDims = tensorShape.GetNumDimensions();
522 dimensions.resize(numDims);
523
524 for (unsigned int outputIdx = 0u; outputIdx < numDims; ++outputIdx)
525 {
526 dimensions[outputIdx] = tensorShape[outputIdx];
527 }
528 outputShapes[i].dimensions = dimensions;
529 outputShapes[i].isSufficient = true;
530 }
531
Mike Kellyb5fdf382019-06-11 16:35:25 +0100532 // run it
533 try
534 {
Mike Kelly65c42dc2019-07-22 14:06:00 +0100535 if (cb.measureTiming == MeasureTiming::YES)
536 {
537 deviceStart = Now();
538 }
539
Mike Kellyb5fdf382019-06-11 16:35:25 +0100540 armnn::Status status = m_Runtime->EnqueueWorkload(m_NetworkId, *pInputTensors, *pOutputTensors);
Mike Kelly65c42dc2019-07-22 14:06:00 +0100541
542 if (cb.measureTiming == MeasureTiming::YES)
543 {
544 deviceEnd = Now();
545 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100546 if (status != armnn::Status::Success)
547 {
548 ALOGW("EnqueueWorkload failed");
Mike Kelly65c42dc2019-07-22 14:06:00 +0100549 cb.callback(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming,
550 "ArmnnPreparedModel_1_2::ExecuteGraph");
Mike Kellyb5fdf382019-06-11 16:35:25 +0100551 return;
552 }
553 }
Kevin May7bdaac52020-02-10 12:10:07 +0000554 catch (armnn::Exception& e)
555 {
556 ALOGW("armnn:Exception caught from EnqueueWorkload: %s", e.what());
557 cb.callback(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_2::ExecuteGraph");
558 return;
559 }
Derek Lambertib9cb8442019-11-28 13:34:48 +0000560 catch (std::exception& e)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100561 {
Kevin May7bdaac52020-02-10 12:10:07 +0000562 ALOGE("std::exception caught from EnqueueWorkload: %s", e.what());
563 cb.callback(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_2::ExecuteGraph");
Mike Kellyb5fdf382019-06-11 16:35:25 +0100564 return;
565 }
566
567 DumpTensorsIfRequired("Output", *pOutputTensors);
568
569 // Commit output buffers.
570 // Note that we update *all* pools, even if they aren't actually used as outputs -
571 // this is simpler and is what the CpuExecutor does.
572 for (android::nn::RunTimePoolInfo& pool : *pMemPools)
573 {
574 pool.update();
575 }
576
Mike Kelly65c42dc2019-07-22 14:06:00 +0100577 if (cb.measureTiming == MeasureTiming::YES)
578 {
579 driverEnd = Now();
580 Timing timing;
581 timing.timeOnDevice = MicrosecondsDuration(deviceEnd, deviceStart);
582 timing.timeInDriver = MicrosecondsDuration(driverEnd, cb.driverStart);
583 cb.callback(ErrorStatus::NONE, outputShapes, timing, "ExecuteGraph");
584 } else {
585 cb.callback(ErrorStatus::NONE, outputShapes, g_NoTiming, "ExecuteGraph");
586 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100587}
588
589template<typename HalVersion>
590bool ArmnnPreparedModel_1_2<HalVersion>::ExecuteWithDummyInputs()
591{
592 std::vector<std::vector<char>> storage;
593 armnn::InputTensors inputTensors;
594 for (unsigned int i = 0; i < m_Model.inputIndexes.size(); i++)
595 {
596 const armnn::TensorInfo inputTensorInfo = m_Runtime->GetInputTensorInfo(m_NetworkId, i);
597 storage.emplace_back(inputTensorInfo.GetNumBytes());
598 const armnn::ConstTensor inputTensor(inputTensorInfo, storage.back().data());
599
600 inputTensors.emplace_back(i, inputTensor);
601 }
602
603 armnn::OutputTensors outputTensors;
604 for (unsigned int i = 0; i < m_Model.outputIndexes.size(); i++)
605 {
606 const armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkId, i);
607 storage.emplace_back(outputTensorInfo.GetNumBytes());
608 const armnn::Tensor outputTensor(outputTensorInfo, storage.back().data());
609
610 outputTensors.emplace_back(i, outputTensor);
611 }
612
613 try
614 {
615 armnn::Status status = m_Runtime->EnqueueWorkload(m_NetworkId, inputTensors, outputTensors);
616 if (status != armnn::Status::Success)
617 {
618 ALOGW("ExecuteWithDummyInputs: EnqueueWorkload failed");
619 return false;
620 }
621 }
Kevin May7bdaac52020-02-10 12:10:07 +0000622 catch (armnn::Exception& e)
623 {
624 ALOGW("ExecuteWithDummyInputs: armnn::Exception caught from EnqueueWorkload: %s", e.what());
625 return false;
626 }
Derek Lambertib9cb8442019-11-28 13:34:48 +0000627 catch (std::exception& e)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100628 {
Kevin May7bdaac52020-02-10 12:10:07 +0000629 ALOGE("ExecuteWithDummyInputs: std::exception caught from EnqueueWorkload: %s", e.what());
Mike Kellyb5fdf382019-06-11 16:35:25 +0100630 return false;
631 }
632 return true;
633}
634
635template<typename HalVersion>
Mike Kellyb5fdf382019-06-11 16:35:25 +0100636Return <ErrorStatus> ArmnnPreparedModel_1_2<HalVersion>::Execute(const Request& request,
Mike Kelly65c42dc2019-07-22 14:06:00 +0100637 MeasureTiming measureTiming,
638 armnnExecuteCallback_1_2 callback)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100639{
Mike Kelly65c42dc2019-07-22 14:06:00 +0100640 TimePoint driverStart;
641
642 if (measureTiming == MeasureTiming::YES)
643 {
644 driverStart = Now();
645 }
646
Mike Kellyb5fdf382019-06-11 16:35:25 +0100647 ALOGV("ArmnnPreparedModel_1_2::execute(): %s", GetModelSummary(m_Model).c_str());
648 m_RequestCount++;
649
Mike Kellyb5fdf382019-06-11 16:35:25 +0100650 if (!android::nn::validateRequest(request, m_Model))
651 {
Mike Kelly65c42dc2019-07-22 14:06:00 +0100652 callback(ErrorStatus::INVALID_ARGUMENT, {}, g_NoTiming, "ArmnnPreparedModel_1_2::execute");
Mike Kellyb5fdf382019-06-11 16:35:25 +0100653 return ErrorStatus::INVALID_ARGUMENT;
654 }
655
656 if (!m_RequestInputsAndOutputsDumpDir.empty())
657 {
Mike Kelly65c42dc2019-07-22 14:06:00 +0100658 ALOGD("Dumping inputs and outputs for request %" PRIuPTR, reinterpret_cast<std::uintptr_t>(&callback));
Mike Kellyb5fdf382019-06-11 16:35:25 +0100659 }
660
661 // allocate the tensors on the heap, as they are passed to the request thread
662 auto pInputTensors = std::make_shared<armnn::InputTensors>();
663 auto pOutputTensors = std::make_shared<armnn::OutputTensors>();
664
665 // map the memory pool into shared pointers
666 // use a shared memory pools vector on the heap, as it is passed to the request thread
667 auto pMemPools = std::make_shared<std::vector<android::nn::RunTimePoolInfo>>();
668
669 if (!setRunTimePoolInfosFromHidlMemories(pMemPools.get(), request.pools))
670 {
Mike Kelly65c42dc2019-07-22 14:06:00 +0100671 callback(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_2::execute");
Mike Kellyb5fdf382019-06-11 16:35:25 +0100672 return ErrorStatus::GENERAL_FAILURE;
673 }
674
675 // add the inputs and outputs with their data
676 try
677 {
678 pInputTensors->reserve(request.inputs.size());
679 for (unsigned int i = 0; i < request.inputs.size(); i++)
680 {
681 const auto& inputArg = request.inputs[i];
682
683 const armnn::TensorInfo inputTensorInfo = m_Runtime->GetInputTensorInfo(m_NetworkId, i);
684 const armnn::Tensor inputTensor = GetTensorForRequestArgument(inputArg, inputTensorInfo, *pMemPools);
685
686 if (inputTensor.GetMemoryArea() == nullptr)
687 {
688 ALOGE("Cannot execute request. Error converting request input %u to tensor", i);
Mike Kelly65c42dc2019-07-22 14:06:00 +0100689 callback(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_2::execute");
Mike Kellyb5fdf382019-06-11 16:35:25 +0100690 return ErrorStatus::GENERAL_FAILURE;
691 }
692
693 pInputTensors->emplace_back(i, inputTensor);
694 }
695
696 pOutputTensors->reserve(request.outputs.size());
Mike Kelly65c42dc2019-07-22 14:06:00 +0100697 std::vector<OutputShape> outputShapes(request.outputs.size());
698
Mike Kellyb5fdf382019-06-11 16:35:25 +0100699 for (unsigned int i = 0; i < request.outputs.size(); i++)
700 {
701 const auto& outputArg = request.outputs[i];
702
703 const armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkId, i);
704 const armnn::Tensor outputTensor = GetTensorForRequestArgument(outputArg, outputTensorInfo, *pMemPools);
705 if (outputTensor.GetMemoryArea() == nullptr)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100706 {
707 ALOGE("Cannot execute request. Error converting request output %u to tensor", i);
Mike Kelly65c42dc2019-07-22 14:06:00 +0100708 callback(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_2::execute");
Mike Kellyb5fdf382019-06-11 16:35:25 +0100709 return ErrorStatus::GENERAL_FAILURE;
710 }
711
Mike Kelly65c42dc2019-07-22 14:06:00 +0100712 const size_t outputSize = outputTensorInfo.GetNumBytes();
713 const size_t bufferSize = pMemPools->at(outputArg.location.poolIndex).getHidlMemory().size();
Mike Kellyb5fdf382019-06-11 16:35:25 +0100714 pOutputTensors->emplace_back(i, outputTensor);
Mike Kelly65c42dc2019-07-22 14:06:00 +0100715
716 hidl_vec<uint32_t> dimensions;
717
718 armnn::TensorShape tensorShape = outputTensorInfo.GetShape();
719 const unsigned int numDims = tensorShape.GetNumDimensions();
720 dimensions.resize(numDims);
721
722 for (unsigned int outputIdx = 0u; outputIdx < numDims; ++outputIdx)
723 {
724 dimensions[outputIdx] = tensorShape[outputIdx];
725 }
726 outputShapes[i].dimensions = dimensions;
727 outputShapes[i].isSufficient = bufferSize >= outputSize;
728
729 if (bufferSize < outputSize)
730 {
731 ALOGW("ArmnnPreparedModel_1_2::Execute failed");
732 callback(ErrorStatus::OUTPUT_INSUFFICIENT_SIZE,
733 outputShapes,
734 g_NoTiming,
735 "ArmnnPreparedModel_1_2::Execute");
736 return ErrorStatus::NONE;
737 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100738 }
739 }
Kevin May7bdaac52020-02-10 12:10:07 +0000740 catch (armnn::Exception& e)
741 {
742 ALOGW("armnn::Exception caught while preparing for EnqueueWorkload: %s", e.what());
743 callback(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_2::execute");
744 return ErrorStatus::GENERAL_FAILURE;
745 }
Derek Lambertib9cb8442019-11-28 13:34:48 +0000746 catch (std::exception& e)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100747 {
Kevin May7bdaac52020-02-10 12:10:07 +0000748 ALOGE("std::exception caught while preparing for EnqueueWorkload: %s", e.what());
Mike Kelly65c42dc2019-07-22 14:06:00 +0100749 callback(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_2::execute");
Mike Kellyb5fdf382019-06-11 16:35:25 +0100750 return ErrorStatus::GENERAL_FAILURE;
751 }
752
753 ALOGV("ArmnnPreparedModel_1_2::execute(...) before PostMsg");
754 // post the request for asynchronous execution
Mike Kelly65c42dc2019-07-22 14:06:00 +0100755 ArmnnCallback_1_2 armnnCb;
756 armnnCb.callback = callback;
757 armnnCb.measureTiming = measureTiming;
758 armnnCb.driverStart = driverStart;
759 m_RequestThread.PostMsg(this, pMemPools, pInputTensors, pOutputTensors, armnnCb);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100760 ALOGV("ArmnnPreparedModel_1_2::execute(...) after PostMsg");
Mike Kellyb5fdf382019-06-11 16:35:25 +0100761 return ErrorStatus::NONE;
762}
763
Mike Kellyb5fdf382019-06-11 16:35:25 +0100764#ifdef ARMNN_ANDROID_NN_V1_2
765template class ArmnnPreparedModel_1_2<hal_1_2::HalPolicy>;
766#endif
767
768} // namespace armnn_driver