blob: 5f3dd6f60c7c4d6a6421884e3f9390806a418838 [file] [log] [blame]
telsoa015307bc12018-03-09 13:51:08 +00001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// See LICENSE file in the project root for full license information.
4//
5
6#define LOG_TAG "ArmnnDriverTests"
7#define BOOST_TEST_MODULE armnn_driver_tests
8#include <boost/test/unit_test.hpp>
9#include <log/log.h>
10
11#include "../ArmnnDriver.hpp"
12#include "../SystemPropertiesUtils.hpp"
13
14#include "OperationsUtils.h"
15
16#include <condition_variable>
17
18namespace android
19{
20namespace hardware
21{
22namespace neuralnetworks
23{
24namespace V1_0
25{
26
27std::ostream& operator<<(std::ostream& os, ErrorStatus stat)
28{
29 return os << static_cast<int>(stat);
30}
31
32}
33}
34}
35}
36
37BOOST_AUTO_TEST_SUITE(DriverTests)
38
39using namespace armnn_driver;
40using namespace android::nn;
41using namespace android;
42
43BOOST_AUTO_TEST_CASE(Init)
44{
45 // Making the driver object on the stack causes a weird libc error, so make it on the heap instead
46 auto driver = std::make_unique<ArmnnDriver>(DriverOptions(armnn::Compute::CpuRef));
47
48 DeviceStatus status = driver->getStatus();
49 // Note double-parentheses to avoid compile error from Boost trying to printf the DeviceStatus
50 BOOST_TEST((status == DeviceStatus::AVAILABLE));
51}
52
53BOOST_AUTO_TEST_CASE(TestCapabilities)
54{
55 // Making the driver object on the stack causes a weird libc error, so make it on the heap instead
56 auto driver = std::make_unique<ArmnnDriver>(DriverOptions(armnn::Compute::CpuRef));
57
58 ErrorStatus error;
59 Capabilities cap;
60
61 ArmnnDriver::getCapabilities_cb cb = [&](ErrorStatus status, const Capabilities& capabilities)
62 {
63 error = status;
64 cap = capabilities;
65 };
66
67 driver->getCapabilities(cb);
68
69 BOOST_TEST((int)error == (int)ErrorStatus::NONE);
70 BOOST_TEST(cap.float32Performance.execTime > 0.f);
71 BOOST_TEST(cap.float32Performance.powerUsage > 0.f);
72 BOOST_TEST(cap.quantized8Performance.execTime > 0.f);
73 BOOST_TEST(cap.quantized8Performance.powerUsage > 0.f);
74}
75
76BOOST_AUTO_TEST_CASE(SystemProperties)
77{
78 // Test default value
79 {
80 auto p = __system_property_find("thisDoesNotExist");
81 BOOST_TEST((p == nullptr));
82
83 int defaultValue = ParseSystemProperty("thisDoesNotExist", -4);
84 BOOST_TEST((defaultValue == -4));
85 }
86
87 // Test default value from bad data type
88 {
89 __system_property_set("thisIsNotFloat", "notfloat");
90 float defaultValue = ParseSystemProperty("thisIsNotFloat", 0.1f);
91 BOOST_TEST((defaultValue == 0.1f));
92 }
93
94 // Test fetching bool values
95 {
96 __system_property_set("myTestBool", "1");
97 bool b = ParseSystemProperty("myTestBool", false);
98 BOOST_TEST((b == true));
99 }
100 {
101 __system_property_set("myTestBool", "0");
102 bool b = ParseSystemProperty("myTestBool", true);
103 BOOST_TEST((b == false));
104 }
105
106 // Test fetching int
107 {
108 __system_property_set("myTestInt", "567");
109 int i = ParseSystemProperty("myTestInt", 890);
110 BOOST_TEST((i==567));
111 }
112
113 // Test fetching float
114 {
115 __system_property_set("myTestFloat", "1.2f");
116 float f = ParseSystemProperty("myTestFloat", 3.4f);
117 BOOST_TEST((f==1.2f));
118 }
119}
120
121// The following are helpers for writing unit tests for the driver
122namespace
123{
124
125struct ExecutionCallback : public IExecutionCallback
126{
127 ExecutionCallback()
128 : mNotified(false)
129 {
130 }
131
132 Return<void> notify(ErrorStatus status) override
133 {
134 (void)status;
135 ALOGI("ExecutionCallback::notify invoked");
136 std::lock_guard<std::mutex> executionLock(mMutex);
137 mNotified = true;
138 mCondition.notify_one();
139 return Void();
140 }
141
142 /// wait until the callback has notified us that it is done
143 Return<void> wait()
144 {
145 ALOGI("ExecutionCallback::wait invoked");
146 std::unique_lock<std::mutex> executionLock(mMutex);
147 while (!mNotified)
148 {
149 mCondition.wait(executionLock);
150 }
151 mNotified = false;
152 return Void();
153 }
154
155private:
156 // use a mutex and a condition variable to wait for asynchronous callbacks
157 std::mutex mMutex;
158 std::condition_variable mCondition;
159 // and a flag, in case we are notified before the wait call
160 bool mNotified;
161};
162
163class PreparedModelCallback : public IPreparedModelCallback
164{
165public:
166 PreparedModelCallback()
167 {
168 }
169
170 ~PreparedModelCallback() override
171 {
172 }
173
174 Return<void> notify(ErrorStatus status, const sp<IPreparedModel>& preparedModel) override
175 {
176 m_ErrorStatus = status;
177 m_PreparedModel = preparedModel;
178 return Void();
179 }
180
181 ErrorStatus GetErrorStatus()
182 {
183 return m_ErrorStatus;
184 }
185
186 sp<IPreparedModel> GetPreparedModel()
187 {
188 return m_PreparedModel;
189 }
190
191
192private:
193 ErrorStatus m_ErrorStatus;
194 sp<IPreparedModel> m_PreparedModel;
195};
196
197
198
199// lifted from common/Utils.cpp
200hidl_memory allocateSharedMemory(int64_t size)
201{
202 hidl_memory memory;
203
204 const std::string& type = "ashmem";
205 android::sp<IAllocator> allocator = IAllocator::getService(type);
206 allocator->allocate(size, [&](bool success, const hidl_memory& mem) {
207 if (!success)
208 {
209 ALOGE("unable to allocate %li bytes of %s", size, type.c_str());
210 }
211 else
212 {
213 memory = mem;
214 }
215 });
216
217 return memory;
218}
219
220
221android::sp<IMemory> AddPoolAndGetData(uint32_t size, Request& request)
222{
223 hidl_memory pool;
224
225 android::sp<IAllocator> allocator = IAllocator::getService("ashmem");
226 allocator->allocate(sizeof(float) * size, [&](bool success, const hidl_memory& mem) {
227 BOOST_TEST(success);
228 pool = mem;
229 });
230
231 request.pools.resize(request.pools.size() + 1);
232 request.pools[request.pools.size() - 1] = pool;
233
234 android::sp<IMemory> mapped = mapMemory(pool);
235 mapped->update();
236 return mapped;
237}
238
239void AddPoolAndSetData(uint32_t size, Request& request, float* data)
240{
241 android::sp<IMemory> memory = AddPoolAndGetData(size, request);
242
243 float* dst = static_cast<float*>(static_cast<void*>(memory->getPointer()));
244
245 memcpy(dst, data, size * sizeof(float));
246}
247
248void AddOperand(Model& model, const Operand& op)
249{
250 model.operands.resize(model.operands.size() + 1);
251 model.operands[model.operands.size() - 1] = op;
252}
253
254void AddIntOperand(Model& model, int32_t value)
255{
256 DataLocation location = {};
257 location.offset = model.operandValues.size();
258 location.length = sizeof(int32_t);
259
260 Operand op = {};
261 op.type = OperandType::INT32;
262 op.dimensions = hidl_vec<uint32_t>{};
263 op.lifetime = OperandLifeTime::CONSTANT_COPY;
264 op.location = location;
265
266 model.operandValues.resize(model.operandValues.size() + location.length);
267 *reinterpret_cast<int32_t*>(&model.operandValues[location.offset]) = value;
268
269 AddOperand(model, op);
270}
271
272template<typename T>
273OperandType TypeToOperandType();
274
275template<>
276OperandType TypeToOperandType<float>()
277{
278 return OperandType::TENSOR_FLOAT32;
279};
280
281template<>
282OperandType TypeToOperandType<int32_t>()
283{
284 return OperandType::TENSOR_INT32;
285};
286
287
288
289template<typename T>
290void AddTensorOperand(Model& model, hidl_vec<uint32_t> dimensions, T* values)
291{
292 uint32_t totalElements = 1;
293 for (uint32_t dim : dimensions)
294 {
295 totalElements *= dim;
296 }
297
298 DataLocation location = {};
299 location.offset = model.operandValues.size();
300 location.length = totalElements * sizeof(T);
301
302 Operand op = {};
303 op.type = TypeToOperandType<T>();
304 op.dimensions = dimensions;
305 op.lifetime = OperandLifeTime::CONSTANT_COPY;
306 op.location = location;
307
308 model.operandValues.resize(model.operandValues.size() + location.length);
309 for (uint32_t i = 0; i < totalElements; i++)
310 {
311 *(reinterpret_cast<T*>(&model.operandValues[location.offset]) + i) = values[i];
312 }
313
314 AddOperand(model, op);
315}
316
317void AddInputOperand(Model& model, hidl_vec<uint32_t> dimensions)
318{
319 Operand op = {};
320 op.type = OperandType::TENSOR_FLOAT32;
321 op.dimensions = dimensions;
322 op.lifetime = OperandLifeTime::MODEL_INPUT;
323
324 AddOperand(model, op);
325
326 model.inputIndexes.resize(model.inputIndexes.size() + 1);
327 model.inputIndexes[model.inputIndexes.size() - 1] = model.operands.size() - 1;
328}
329
330void AddOutputOperand(Model& model, hidl_vec<uint32_t> dimensions)
331{
332 Operand op = {};
333 op.type = OperandType::TENSOR_FLOAT32;
334 op.dimensions = dimensions;
335 op.lifetime = OperandLifeTime::MODEL_OUTPUT;
336
337 AddOperand(model, op);
338
339 model.outputIndexes.resize(model.outputIndexes.size() + 1);
340 model.outputIndexes[model.outputIndexes.size() - 1] = model.operands.size() - 1;
341}
342
343android::sp<IPreparedModel> PrepareModel(const Model& model, ArmnnDriver& driver)
344{
345
346 sp<PreparedModelCallback> cb(new PreparedModelCallback());
347 driver.prepareModel(model, cb);
348
349 BOOST_TEST((cb->GetErrorStatus() == ErrorStatus::NONE));
350 BOOST_TEST((cb->GetPreparedModel() != nullptr));
351
352 return cb->GetPreparedModel();
353}
354
355void Execute(android::sp<IPreparedModel> preparedModel, const Request& request)
356{
357 sp<ExecutionCallback> cb(new ExecutionCallback());
358 BOOST_TEST(preparedModel->execute(request, cb) == ErrorStatus::NONE);
359 ALOGI("Execute: waiting for callback to be invoked");
360 cb->wait();
361}
362
363sp<ExecutionCallback> ExecuteNoWait(android::sp<IPreparedModel> preparedModel, const Request& request)
364{
365 sp<ExecutionCallback> cb(new ExecutionCallback());
366 BOOST_TEST(preparedModel->execute(request, cb) == ErrorStatus::NONE);
367 ALOGI("ExecuteNoWait: returning callback object");
368 return cb;
369}
370}
371
372// Add our own test here since we fail the fc tests which Google supplies (because of non-const weights)
373BOOST_AUTO_TEST_CASE(FullyConnected)
374{
375 // this should ideally replicate fully_connected_float.model.cpp
376 // but that uses slightly weird dimensions which I don't think we need to support for now
377
378 auto driver = std::make_unique<ArmnnDriver>(DriverOptions(armnn::Compute::CpuRef));
379 Model model = {};
380
381 // add operands
382 int32_t actValue = 0;
383 float weightValue[] = {2, 4, 1};
384 float biasValue[] = {4};
385
386 AddInputOperand(model, hidl_vec<uint32_t>{1, 3});
387 AddTensorOperand(model, hidl_vec<uint32_t>{1, 3}, weightValue);
388 AddTensorOperand(model, hidl_vec<uint32_t>{1}, biasValue);
389 AddIntOperand(model, actValue);
390 AddOutputOperand(model, hidl_vec<uint32_t>{1, 1});
391
392 // make the fully connected operation
393 model.operations.resize(1);
394 model.operations[0].type = OperationType::FULLY_CONNECTED;
395 model.operations[0].inputs = hidl_vec<uint32_t>{0, 1, 2, 3};
396 model.operations[0].outputs = hidl_vec<uint32_t>{4};
397
398 // make the prepared model
399 android::sp<IPreparedModel> preparedModel = PrepareModel(model, *driver);
400
401 // construct the request
402 DataLocation inloc = {};
403 inloc.poolIndex = 0;
404 inloc.offset = 0;
405 inloc.length = 3 * sizeof(float);
406 RequestArgument input = {};
407 input.location = inloc;
408 input.dimensions = hidl_vec<uint32_t>{};
409
410 DataLocation outloc = {};
411 outloc.poolIndex = 1;
412 outloc.offset = 0;
413 outloc.length = 1 * sizeof(float);
414 RequestArgument output = {};
415 output.location = outloc;
416 output.dimensions = hidl_vec<uint32_t>{};
417
418 Request request = {};
419 request.inputs = hidl_vec<RequestArgument>{input};
420 request.outputs = hidl_vec<RequestArgument>{output};
421
422 // set the input data (matching source test)
423 float indata[] = {2, 32, 16};
424 AddPoolAndSetData(3, request, indata);
425
426 // add memory for the output
427 android::sp<IMemory> outMemory = AddPoolAndGetData(1, request);
428 float* outdata = static_cast<float*>(static_cast<void*>(outMemory->getPointer()));
429
430 // run the execution
431 Execute(preparedModel, request);
432
433 // check the result
434 BOOST_TEST(outdata[0] == 152);
435}
436
437// Add our own test for concurrent execution
438// The main point of this test is to check that multiple requests can be
439// executed without waiting for the callback from previous execution.
440// The operations performed are not significant.
441BOOST_AUTO_TEST_CASE(ConcurrentExecute)
442{
443 ALOGI("ConcurrentExecute: entry");
444
445 auto driver = std::make_unique<ArmnnDriver>(DriverOptions(armnn::Compute::CpuRef));
446 Model model = {};
447
448 // add operands
449 int32_t actValue = 0;
450 float weightValue[] = {2, 4, 1};
451 float biasValue[] = {4};
452
453 AddInputOperand(model, hidl_vec<uint32_t>{1, 3});
454 AddTensorOperand(model, hidl_vec<uint32_t>{1, 3}, weightValue);
455 AddTensorOperand(model, hidl_vec<uint32_t>{1}, biasValue);
456 AddIntOperand(model, actValue);
457 AddOutputOperand(model, hidl_vec<uint32_t>{1, 1});
458
459 // make the fully connected operation
460 model.operations.resize(1);
461 model.operations[0].type = OperationType::FULLY_CONNECTED;
462 model.operations[0].inputs = hidl_vec<uint32_t>{0, 1, 2, 3};
463 model.operations[0].outputs = hidl_vec<uint32_t>{4};
464
465 // make the prepared models
466 const size_t maxRequests = 5;
467 android::sp<IPreparedModel> preparedModels[maxRequests];
468 for (size_t i = 0; i < maxRequests; ++i)
469 {
470 preparedModels[i] = PrepareModel(model, *driver);
471 }
472
473 // construct the request data
474 DataLocation inloc = {};
475 inloc.poolIndex = 0;
476 inloc.offset = 0;
477 inloc.length = 3 * sizeof(float);
478 RequestArgument input = {};
479 input.location = inloc;
480 input.dimensions = hidl_vec<uint32_t>{};
481
482 DataLocation outloc = {};
483 outloc.poolIndex = 1;
484 outloc.offset = 0;
485 outloc.length = 1 * sizeof(float);
486 RequestArgument output = {};
487 output.location = outloc;
488 output.dimensions = hidl_vec<uint32_t>{};
489
490 // build the requests
491 Request requests[maxRequests];
492 android::sp<IMemory> outMemory[maxRequests];
493 float* outdata[maxRequests];
494 for (size_t i = 0; i < maxRequests; ++i)
495 {
496 requests[i].inputs = hidl_vec<RequestArgument>{input};
497 requests[i].outputs = hidl_vec<RequestArgument>{output};
498 // set the input data (matching source test)
499 float indata[] = {2, 32, 16};
500 AddPoolAndSetData(3, requests[i], indata);
501 // add memory for the output
502 outMemory[i] = AddPoolAndGetData(1, requests[i]);
503 outdata[i] = static_cast<float*>(static_cast<void*>(outMemory[i]->getPointer()));
504 }
505
506 // invoke the execution of the requests
507 ALOGI("ConcurrentExecute: executing requests");
508 sp<ExecutionCallback> cb[maxRequests];
509 for (size_t i = 0; i < maxRequests; ++i)
510 {
511 cb[i] = ExecuteNoWait(preparedModels[i], requests[i]);
512 }
513
514 // wait for the requests to complete
515 ALOGI("ConcurrentExecute: waiting for callbacks");
516 for (size_t i = 0; i < maxRequests; ++i)
517 {
518 cb[i]->wait();
519 }
520
521 // check the results
522 ALOGI("ConcurrentExecute: validating results");
523 for (size_t i = 0; i < maxRequests; ++i)
524 {
525 BOOST_TEST(outdata[i][0] == 152);
526 }
527 ALOGI("ConcurrentExecute: exit");
528}
529
530BOOST_AUTO_TEST_CASE(GetSupportedOperations)
531{
532 auto driver = std::make_unique<ArmnnDriver>(DriverOptions(armnn::Compute::CpuRef));
533
534 ErrorStatus error;
535 std::vector<bool> sup;
536
537 ArmnnDriver::getSupportedOperations_cb cb = [&](ErrorStatus status, const std::vector<bool>& supported)
538 {
539 error = status;
540 sup = supported;
541 };
542
543 Model model1 = {};
544
545 // add operands
546 int32_t actValue = 0;
547 float weightValue[] = {2, 4, 1};
548 float biasValue[] = {4};
549
550 AddInputOperand(model1, hidl_vec<uint32_t>{1, 3});
551 AddTensorOperand(model1, hidl_vec<uint32_t>{1, 3}, weightValue);
552 AddTensorOperand(model1, hidl_vec<uint32_t>{1}, biasValue);
553 AddIntOperand(model1, actValue);
554 AddOutputOperand(model1, hidl_vec<uint32_t>{1, 1});
555
556 // make a correct fully connected operation
557 model1.operations.resize(2);
558 model1.operations[0].type = OperationType::FULLY_CONNECTED;
559 model1.operations[0].inputs = hidl_vec<uint32_t>{0, 1, 2, 3};
560 model1.operations[0].outputs = hidl_vec<uint32_t>{4};
561
562 // make an incorrect fully connected operation
563 AddIntOperand(model1, actValue);
564 AddOutputOperand(model1, hidl_vec<uint32_t>{1, 1});
565 model1.operations[1].type = OperationType::FULLY_CONNECTED;
566 model1.operations[1].inputs = hidl_vec<uint32_t>{4};
567 model1.operations[1].outputs = hidl_vec<uint32_t>{5};
568
569 driver->getSupportedOperations(model1, cb);
570 BOOST_TEST((int)error == (int)ErrorStatus::NONE);
571 BOOST_TEST(sup[0] == true);
572 BOOST_TEST(sup[1] == false);
573
574 // Broadcast add/mul are not supported
575 Model model2 = {};
576
577 AddInputOperand(model2, hidl_vec<uint32_t>{1, 1, 3, 4});
578 AddInputOperand(model2, hidl_vec<uint32_t>{4});
579 AddOutputOperand(model2, hidl_vec<uint32_t>{1, 1, 3, 4});
580 AddOutputOperand(model2, hidl_vec<uint32_t>{1, 1, 3, 4});
581
582 model2.operations.resize(2);
583
584 model2.operations[0].type = OperationType::ADD;
585 model2.operations[0].inputs = hidl_vec<uint32_t>{0,1};
586 model2.operations[0].outputs = hidl_vec<uint32_t>{2};
587
588 model2.operations[1].type = OperationType::MUL;
589 model2.operations[1].inputs = hidl_vec<uint32_t>{0,1};
590 model2.operations[1].outputs = hidl_vec<uint32_t>{3};
591
592 driver->getSupportedOperations(model2, cb);
593 BOOST_TEST((int)error == (int)ErrorStatus::NONE);
594 BOOST_TEST(sup[0] == false);
595 BOOST_TEST(sup[1] == false);
596
597 Model model3 = {};
598
599 // Add unsupported operation, should return no error but we don't support it
600 AddInputOperand(model3, hidl_vec<uint32_t>{1, 1, 1, 8});
601 AddIntOperand(model3, 2);
602 AddOutputOperand(model3, hidl_vec<uint32_t>{1, 2, 2, 2});
603 model3.operations.resize(1);
604 model3.operations[0].type = OperationType::DEPTH_TO_SPACE;
605 model1.operations[0].inputs = hidl_vec<uint32_t>{0, 1};
606 model3.operations[0].outputs = hidl_vec<uint32_t>{2};
607
608 driver->getSupportedOperations(model3, cb);
609 BOOST_TEST((int)error == (int)ErrorStatus::NONE);
610 BOOST_TEST(sup[0] == false);
611
612 // Add invalid operation
613 Model model4 = {};
614 AddIntOperand(model4, 0);
615 model4.operations.resize(1);
616 model4.operations[0].type = static_cast<OperationType>(100);
617 model4.operations[0].outputs = hidl_vec<uint32_t>{0};
618
619 driver->getSupportedOperations(model4, cb);
620 BOOST_TEST((int)error == (int)ErrorStatus::INVALID_ARGUMENT);
621}
622
623// The purpose of this test is to ensure that when encountering an unsupported operation
624// it is skipped and getSupportedOperations() continues (rather than failing and stopping).
625// As per IVGCVSW-710.
626BOOST_AUTO_TEST_CASE(UnsupportedLayerContinueOnFailure)
627{
628 auto driver = std::make_unique<ArmnnDriver>(DriverOptions(armnn::Compute::CpuRef));
629
630 ErrorStatus error;
631 std::vector<bool> sup;
632
633 ArmnnDriver::getSupportedOperations_cb cb = [&](ErrorStatus status, const std::vector<bool>& supported)
634 {
635 error = status;
636 sup = supported;
637 };
638
639 Model model = {};
640
641 // operands
642 int32_t actValue = 0;
643 float weightValue[] = {2, 4, 1};
644 float biasValue[] = {4};
645
646 // broadcast add is unsupported at the time of writing this test, but any unsupported layer will do
647 AddInputOperand(model, hidl_vec<uint32_t>{1, 1, 3, 4});
648 AddInputOperand(model, hidl_vec<uint32_t>{4});
649 AddOutputOperand(model, hidl_vec<uint32_t>{1, 1, 3, 4});
650
651 // fully connected
652 AddInputOperand(model, hidl_vec<uint32_t>{1, 3});
653 AddTensorOperand(model, hidl_vec<uint32_t>{1, 3}, weightValue);
654 AddTensorOperand(model, hidl_vec<uint32_t>{1}, biasValue);
655 AddIntOperand(model, actValue);
656 AddOutputOperand(model, hidl_vec<uint32_t>{1, 1});
657
658 // broadcast mul is unsupported
659 AddOutputOperand(model, hidl_vec<uint32_t>{1, 1, 3, 4});
660
661 model.operations.resize(3);
662
663 // unsupported
664 model.operations[0].type = OperationType::ADD;
665 model.operations[0].inputs = hidl_vec<uint32_t>{0,1};
666 model.operations[0].outputs = hidl_vec<uint32_t>{2};
667
668 // supported
669 model.operations[1].type = OperationType::FULLY_CONNECTED;
670 model.operations[1].inputs = hidl_vec<uint32_t>{3, 4, 5, 6};
671 model.operations[1].outputs = hidl_vec<uint32_t>{7};
672
673 // unsupported
674 model.operations[2].type = OperationType::MUL;
675 model.operations[2].inputs = hidl_vec<uint32_t>{0,1};
676 model.operations[2].outputs = hidl_vec<uint32_t>{8};
677
678 // we are testing that the unsupported layers return false and the test continues
679 // rather than failing and stopping.
680 driver->getSupportedOperations(model, cb);
681 BOOST_TEST((int)error == (int)ErrorStatus::NONE);
682 BOOST_TEST(sup[0] == false);
683 BOOST_TEST(sup[1] == true);
684 BOOST_TEST(sup[2] == false);
685}
686
687// The purpose of this test is to ensure that when encountering an failure
688// during mem pool mapping we properly report an error to the framework via a callback
689BOOST_AUTO_TEST_CASE(ModelToINetworkConverterMemPoolFail)
690{
691 auto driver = std::make_unique<ArmnnDriver>(armnn::Compute::CpuRef);
692
693 ErrorStatus error;
694 std::vector<bool> sup;
695
696 ArmnnDriver::getSupportedOperations_cb cb = [&](ErrorStatus status, const std::vector<bool>& supported)
697 {
698 error = status;
699 sup = supported;
700 };
701
702 Model model = {};
703
704 model.pools = hidl_vec<hidl_memory>{hidl_memory("Unsuported hidl memory type", nullptr, 0)};
705
706 //memory pool mapping should fail, we should report an error
707 driver->getSupportedOperations(model, cb);
708 BOOST_TEST((int)error == (int)ErrorStatus::GENERAL_FAILURE);
709}
710
711namespace
712{
713
714void PaddingTestImpl(android::nn::PaddingScheme paddingScheme)
715{
716 auto driver = std::make_unique<ArmnnDriver>(DriverOptions(armnn::Compute::CpuRef));
717 Model model = {};
718
719 uint32_t outSize = paddingScheme == kPaddingSame ? 2 : 1;
720
721 // add operands
722 float weightValue[] = {1, -1, 0, 1};
723 float biasValue[] = {0};
724
725 AddInputOperand(model, hidl_vec<uint32_t>{1, 2, 3, 1});
726 AddTensorOperand(model, hidl_vec<uint32_t>{1, 2, 2, 1}, weightValue);
727 AddTensorOperand(model, hidl_vec<uint32_t>{1}, biasValue);
728 AddIntOperand(model, (int32_t)paddingScheme); // padding
729 AddIntOperand(model, 2); // stride x
730 AddIntOperand(model, 2); // stride y
731 AddIntOperand(model, 0); // no activation
732 AddOutputOperand(model, hidl_vec<uint32_t>{1, 1, outSize, 1});
733
734 // make the convolution operation
735 model.operations.resize(1);
736 model.operations[0].type = OperationType::CONV_2D;
737 model.operations[0].inputs = hidl_vec<uint32_t>{0, 1, 2, 3, 4, 5, 6};
738 model.operations[0].outputs = hidl_vec<uint32_t>{7};
739
740 // make the prepared model
741 android::sp<IPreparedModel> preparedModel = PrepareModel(model, *driver);
742
743 // construct the request
744 DataLocation inloc = {};
745 inloc.poolIndex = 0;
746 inloc.offset = 0;
747 inloc.length = 6 * sizeof(float);
748 RequestArgument input = {};
749 input.location = inloc;
750 input.dimensions = hidl_vec<uint32_t>{};
751
752 DataLocation outloc = {};
753 outloc.poolIndex = 1;
754 outloc.offset = 0;
755 outloc.length = outSize * sizeof(float);
756 RequestArgument output = {};
757 output.location = outloc;
758 output.dimensions = hidl_vec<uint32_t>{};
759
760 Request request = {};
761 request.inputs = hidl_vec<RequestArgument>{input};
762 request.outputs = hidl_vec<RequestArgument>{output};
763
764
765 // set the input data (matching source test)
766 float indata[] = {4, 1, 0, 3, -1, 2};
767 AddPoolAndSetData(6, request, indata);
768
769 // add memory for the output
770 android::sp<IMemory> outMemory = AddPoolAndGetData(outSize, request);
771 float* outdata = static_cast<float*>(static_cast<void*>(outMemory->getPointer()));
772
773 // run the execution
774 Execute(preparedModel, request);
775
776 // check the result
777 if (paddingScheme == kPaddingValid)
778 {
779 BOOST_TEST(outdata[0] == 2);
780 }
781 else if (paddingScheme == kPaddingSame)
782 {
783 BOOST_TEST(outdata[0] == 2);
784 BOOST_TEST(outdata[1] == 0);
785 }
786 else
787 {
788 BOOST_TEST(false);
789 }
790}
791
792}
793
794BOOST_AUTO_TEST_CASE(ConvValidPadding)
795{
796 PaddingTestImpl(kPaddingValid);
797}
798
799BOOST_AUTO_TEST_CASE(ConvSamePadding)
800{
801 PaddingTestImpl(kPaddingSame);
802}
803
804BOOST_AUTO_TEST_CASE(TestFullyConnected4dInput)
805{
806 auto driver = std::make_unique<ArmnnDriver>(DriverOptions(armnn::Compute::CpuRef));
807
808 ErrorStatus error;
809 std::vector<bool> sup;
810
811 ArmnnDriver::getSupportedOperations_cb cb = [&](ErrorStatus status, const std::vector<bool>& supported)
812 {
813 error = status;
814 sup = supported;
815 };
816
817 Model model = {};
818
819 // operands
820 int32_t actValue = 0;
821 float weightValue[] = {1, 0, 0, 0, 0, 0, 0, 0,
822 0, 1, 0, 0, 0, 0, 0, 0,
823 0, 0, 1, 0, 0, 0, 0, 0,
824 0, 0, 0, 1, 0, 0, 0, 0,
825 0, 0, 0, 0, 1, 0, 0, 0,
826 0, 0, 0, 0, 0, 1, 0, 0,
827 0, 0, 0, 0, 0, 0, 1, 0,
828 0, 0, 0, 0, 0, 0, 0, 1}; //identity
829 float biasValue[] = {0, 0, 0, 0, 0, 0, 0, 0};
830
831 // fully connected operation
832 AddInputOperand(model, hidl_vec<uint32_t>{1, 1, 1, 8});
833 AddTensorOperand(model, hidl_vec<uint32_t>{8, 8}, weightValue);
834 AddTensorOperand(model, hidl_vec<uint32_t>{8}, biasValue);
835 AddIntOperand(model, actValue);
836 AddOutputOperand(model, hidl_vec<uint32_t>{1, 8});
837
838 model.operations.resize(1);
839
840 model.operations[0].type = OperationType::FULLY_CONNECTED;
841 model.operations[0].inputs = hidl_vec<uint32_t>{0,1,2,3};
842 model.operations[0].outputs = hidl_vec<uint32_t>{4};
843
844 // make the prepared model
845 android::sp<IPreparedModel> preparedModel = PrepareModel(model, *driver);
846
847
848 // construct the request
849 DataLocation inloc = {};
850 inloc.poolIndex = 0;
851 inloc.offset = 0;
852 inloc.length = 8 * sizeof(float);
853 RequestArgument input = {};
854 input.location = inloc;
855 input.dimensions = hidl_vec<uint32_t>{};
856
857 DataLocation outloc = {};
858 outloc.poolIndex = 1;
859 outloc.offset = 0;
860 outloc.length = 8 * sizeof(float);
861 RequestArgument output = {};
862 output.location = outloc;
863 output.dimensions = hidl_vec<uint32_t>{};
864
865 Request request = {};
866 request.inputs = hidl_vec<RequestArgument>{input};
867 request.outputs = hidl_vec<RequestArgument>{output};
868
869 // set the input data
870 float indata[] = {1,2,3,4,5,6,7,8};
871 AddPoolAndSetData(8, request, indata);
872
873 // add memory for the output
874 android::sp<IMemory> outMemory = AddPoolAndGetData(8, request);
875 float* outdata = static_cast<float*>(static_cast<void*>(outMemory->getPointer()));
876
877 // run the execution
878 Execute(preparedModel, request);
879
880 // check the result
881 BOOST_TEST(outdata[0] == 1);
882 BOOST_TEST(outdata[1] == 2);
883 BOOST_TEST(outdata[2] == 3);
884 BOOST_TEST(outdata[3] == 4);
885 BOOST_TEST(outdata[4] == 5);
886 BOOST_TEST(outdata[5] == 6);
887 BOOST_TEST(outdata[6] == 7);
888 BOOST_TEST(outdata[7] == 8);
889}
890
891BOOST_AUTO_TEST_CASE(TestFullyConnected4dInputReshape)
892{
893 auto driver = std::make_unique<ArmnnDriver>(DriverOptions(armnn::Compute::CpuRef));
894
895 ErrorStatus error;
896 std::vector<bool> sup;
897
898 ArmnnDriver::getSupportedOperations_cb cb = [&](ErrorStatus status, const std::vector<bool>& supported)
899 {
900 error = status;
901 sup = supported;
902 };
903
904 Model model = {};
905
906 // operands
907 int32_t actValue = 0;
908 float weightValue[] = {1, 0, 0, 0, 0, 0, 0, 0,
909 0, 1, 0, 0, 0, 0, 0, 0,
910 0, 0, 1, 0, 0, 0, 0, 0,
911 0, 0, 0, 1, 0, 0, 0, 0,
912 0, 0, 0, 0, 1, 0, 0, 0,
913 0, 0, 0, 0, 0, 1, 0, 0,
914 0, 0, 0, 0, 0, 0, 1, 0,
915 0, 0, 0, 0, 0, 0, 0, 1}; //identity
916 float biasValue[] = {0, 0, 0, 0, 0, 0, 0, 0};
917
918 // fully connected operation
919 AddInputOperand(model, hidl_vec<uint32_t>{1, 2, 2, 2});
920 AddTensorOperand(model, hidl_vec<uint32_t>{8, 8}, weightValue);
921 AddTensorOperand(model, hidl_vec<uint32_t>{8}, biasValue);
922 AddIntOperand(model, actValue);
923 AddOutputOperand(model, hidl_vec<uint32_t>{1, 8});
924
925 model.operations.resize(1);
926
927 model.operations[0].type = OperationType::FULLY_CONNECTED;
928 model.operations[0].inputs = hidl_vec<uint32_t>{0,1,2,3};
929 model.operations[0].outputs = hidl_vec<uint32_t>{4};
930
931 // make the prepared model
932 android::sp<IPreparedModel> preparedModel = PrepareModel(model, *driver);
933
934
935 // construct the request
936 DataLocation inloc = {};
937 inloc.poolIndex = 0;
938 inloc.offset = 0;
939 inloc.length = 8 * sizeof(float);
940 RequestArgument input = {};
941 input.location = inloc;
942 input.dimensions = hidl_vec<uint32_t>{};
943
944 DataLocation outloc = {};
945 outloc.poolIndex = 1;
946 outloc.offset = 0;
947 outloc.length = 8 * sizeof(float);
948 RequestArgument output = {};
949 output.location = outloc;
950 output.dimensions = hidl_vec<uint32_t>{};
951
952 Request request = {};
953 request.inputs = hidl_vec<RequestArgument>{input};
954 request.outputs = hidl_vec<RequestArgument>{output};
955
956 // set the input data
957 float indata[] = {1,2,3,4,5,6,7,8};
958 AddPoolAndSetData(8, request, indata);
959
960 // add memory for the output
961 android::sp<IMemory> outMemory = AddPoolAndGetData(8, request);
962 float* outdata = static_cast<float*>(static_cast<void*>(outMemory->getPointer()));
963
964 // run the execution
965 Execute(preparedModel, request);
966
967 // check the result
968 BOOST_TEST(outdata[0] == 1);
969 BOOST_TEST(outdata[1] == 2);
970 BOOST_TEST(outdata[2] == 3);
971 BOOST_TEST(outdata[3] == 4);
972 BOOST_TEST(outdata[4] == 5);
973 BOOST_TEST(outdata[5] == 6);
974 BOOST_TEST(outdata[6] == 7);
975 BOOST_TEST(outdata[7] == 8);
976}
977
978BOOST_AUTO_TEST_SUITE_END()