1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <android/hardware/neuralnetworks/1.2/ADevice.h>
18 #include <gtest/gtest.h>
19
20 #include <algorithm>
21 #include <map>
22 #include <set>
23 #include <string>
24 #include <tuple>
25 #include <utility>
26 #include <vector>
27
28 #include "HalInterfaces.h"
29 #include "Manager.h"
30 #include "Memory.h"
31 #include "SampleDriver.h"
32 #include "SampleDriverFull.h"
33 #include "TestNeuralNetworksWrapper.h"
34 #include "TestUtils.h"
35
36 using namespace android::nn;
37 using namespace hal;
38 using Result = test_wrapper::Result;
39 using Type = test_wrapper::Type;
40
41 namespace {
42
43 // A buffer for test that does nothing.
44 class TestBuffer : public IBuffer {
45 public:
copyTo(const hidl_memory &)46 Return<ErrorStatus> copyTo(const hidl_memory&) override {
47 return ErrorStatus::DEVICE_UNAVAILABLE;
48 }
copyFrom(const hidl_memory &,const hidl_vec<uint32_t> &)49 Return<ErrorStatus> copyFrom(const hidl_memory&, const hidl_vec<uint32_t>&) override {
50 return ErrorStatus::DEVICE_UNAVAILABLE;
51 }
52 };
53
54 enum class AllocateReturn { OK, BAD_TOKEN, BAD_IBUFFER, BAD_STATUS, NOT_SUPPORTED };
55
56 // Print AllocateReturn enum for better GTEST failure messages
operator <<(std::ostream & os,AllocateReturn allocateReturn)57 std::ostream& operator<<(std::ostream& os, AllocateReturn allocateReturn) {
58 switch (allocateReturn) {
59 case AllocateReturn::OK:
60 return os << "OK";
61 case AllocateReturn::BAD_IBUFFER:
62 return os << "BAD_IBUFFER";
63 case AllocateReturn::BAD_TOKEN:
64 return os << "BAD_TOKEN";
65 case AllocateReturn::BAD_STATUS:
66 return os << "BAD_STATUS";
67 case AllocateReturn::NOT_SUPPORTED:
68 return os << "NOT_SUPPORTED";
69 }
70 LOG(FATAL) << "Invalid AllocateReturn code " << static_cast<int>(allocateReturn);
71 return os;
72 }
73
74 class TestDriverLatest : public sample_driver::SampleDriver {
75 public:
TestDriverLatest(const char * name,std::set<OperationType> supportedOperations,AllocateReturn allocateReturn)76 TestDriverLatest(const char* name, std::set<OperationType> supportedOperations,
77 AllocateReturn allocateReturn)
78 : SampleDriver(name),
79 kSupportedOperations(std::move(supportedOperations)),
80 kAllocateReturn(allocateReturn) {}
81
getCapabilities_1_3(getCapabilities_1_3_cb cb)82 Return<void> getCapabilities_1_3(getCapabilities_1_3_cb cb) override {
83 android::nn::initVLogMask();
84 // Faster than cpu.
85 const PerformanceInfo kPerf = {.execTime = 0.1, .powerUsage = 0.1};
86 const Capabilities capabilities = {
87 .relaxedFloat32toFloat16PerformanceScalar = kPerf,
88 .relaxedFloat32toFloat16PerformanceTensor = kPerf,
89 .operandPerformance = nonExtensionOperandPerformance<HalVersion::V1_3>(kPerf),
90 .ifPerformance = kPerf,
91 .whilePerformance = kPerf};
92 cb(ErrorStatus::NONE, capabilities);
93 return Void();
94 }
95
getSupportedOperations_1_3(const Model & model,getSupportedOperations_1_3_cb cb)96 Return<void> getSupportedOperations_1_3(const Model& model,
97 getSupportedOperations_1_3_cb cb) override {
98 // The tests will never use a referenced model.
99 CHECK(model.referenced.size() == 0);
100 std::vector<bool> supported(model.main.operations.size(), false);
101 std::transform(
102 model.main.operations.begin(), model.main.operations.end(), supported.begin(),
103 [this](const Operation& op) { return kSupportedOperations.count(op.type) > 0; });
104 cb(ErrorStatus::NONE, supported);
105 return Void();
106 }
107
allocate(const BufferDesc &,const hidl_vec<sp<IPreparedModel>> &,const hidl_vec<BufferRole> &,const hidl_vec<BufferRole> &,allocate_cb cb)108 Return<void> allocate(const BufferDesc&, const hidl_vec<sp<IPreparedModel>>&,
109 const hidl_vec<BufferRole>&, const hidl_vec<BufferRole>&,
110 allocate_cb cb) override {
111 switch (kAllocateReturn) {
112 case AllocateReturn::OK:
113 cb(ErrorStatus::NONE, new TestBuffer(), mValidBufferToken++);
114 return Void();
115 case AllocateReturn::BAD_IBUFFER:
116 cb(ErrorStatus::NONE, nullptr, mValidBufferToken++);
117 return Void();
118 case AllocateReturn::BAD_TOKEN:
119 cb(ErrorStatus::NONE, new TestBuffer(), 0);
120 return Void();
121 case AllocateReturn::BAD_STATUS:
122 cb(ErrorStatus::GENERAL_FAILURE, new TestBuffer(), mValidBufferToken++);
123 return Void();
124 case AllocateReturn::NOT_SUPPORTED:
125 cb(ErrorStatus::GENERAL_FAILURE, nullptr, 0);
126 return Void();
127 }
128 LOG(FATAL) << "Invalid AllocateReturn code " << static_cast<int>(kAllocateReturn);
129 return Void();
130 }
131
132 private:
133 const std::set<OperationType> kSupportedOperations;
134 const AllocateReturn kAllocateReturn;
135 uint32_t mValidBufferToken = 1;
136 };
137
138 // Create the following model for test.
139 //
140 // input0 ---+
141 // +--- ADD ---> output0 ---+
142 // input1 ---+ +--- MUL ---> output1 (dynamic shape)
143 // +--- SUB ---> temp ---+
144 // input2 ---+
145 //
createTestModel()146 test_wrapper::Model createTestModel() {
147 test_wrapper::Model model;
148 test_wrapper::OperandType tensorTypeFullySpecified(Type::TENSOR_FLOAT32, {1});
149 test_wrapper::OperandType tensorTypeDynamicShape(Type::TENSOR_FLOAT32, {0});
150 test_wrapper::OperandType actType(Type::INT32, {});
151 uint32_t input0 = model.addOperand(&tensorTypeFullySpecified);
152 uint32_t input1 = model.addOperand(&tensorTypeFullySpecified);
153 uint32_t input2 = model.addOperand(&tensorTypeFullySpecified);
154 uint32_t temp = model.addOperand(&tensorTypeFullySpecified);
155 uint32_t output0 = model.addOperand(&tensorTypeFullySpecified);
156 uint32_t output1 = model.addOperand(&tensorTypeDynamicShape);
157 uint32_t act = model.addOperand(&actType);
158 int32_t activation = 0;
159 model.setOperandValue(act, &activation, sizeof(int32_t));
160 model.addOperation(ANEURALNETWORKS_ADD, {input0, input1, act}, {output0});
161 model.addOperation(ANEURALNETWORKS_SUB, {input1, input2, act}, {temp});
162 model.addOperation(ANEURALNETWORKS_MUL, {output0, temp, act}, {output1});
163 model.identifyInputsAndOutputs({input0, input1, input2}, {output0, output1});
164 EXPECT_EQ(model.finish(), Result::NO_ERROR);
165 return model;
166 }
167
168 class MemoryDomainTestBase : public ::testing::Test {
169 protected:
SetUp()170 void SetUp() override {
171 ::testing::Test::SetUp();
172 if (DeviceManager::get()->getUseCpuOnly()) {
173 GTEST_SKIP();
174 }
175 // Clear the device list.
176 DeviceManager::get()->forTest_setDevices({});
177 }
178
TearDown()179 void TearDown() override {
180 DeviceManager::get()->forTest_reInitializeDeviceList();
181 ::testing::Test::TearDown();
182 }
183
184 // If "deviceNames" is not empty, the compilation is created with explicit device list;
185 // otherwise, it is created normally.
createCompilation(const std::vector<std::string> & deviceNames)186 test_wrapper::Compilation createCompilation(const std::vector<std::string>& deviceNames) {
187 test_wrapper::Compilation compilation;
188 if (!deviceNames.empty()) {
189 // Map device names to ANeuralNetworksDevice.
190 std::map<std::string, ANeuralNetworksDevice*> deviceMap;
191 uint32_t numDevices = 0;
192 EXPECT_EQ(ANeuralNetworks_getDeviceCount(&numDevices), ANEURALNETWORKS_NO_ERROR);
193 for (uint32_t i = 0; i < numDevices; i++) {
194 ANeuralNetworksDevice* device = nullptr;
195 const char* name = nullptr;
196 EXPECT_EQ(ANeuralNetworks_getDevice(i, &device), ANEURALNETWORKS_NO_ERROR);
197 EXPECT_EQ(ANeuralNetworksDevice_getName(device, &name), ANEURALNETWORKS_NO_ERROR);
198 deviceMap.emplace(name, device);
199 }
200 std::vector<const ANeuralNetworksDevice*> devices(deviceNames.size());
201 std::transform(deviceNames.begin(), deviceNames.end(), devices.begin(),
202 [&deviceMap](const std::string& name) { return deviceMap.at(name); });
203 Result result;
204 std::tie(result, compilation) =
205 test_wrapper::Compilation::createForDevices(&kModel, devices);
206 EXPECT_EQ(result, Result::NO_ERROR);
207 } else {
208 compilation = test_wrapper::Compilation(&kModel);
209 }
210 EXPECT_EQ(compilation.finish(), Result::NO_ERROR);
211 return compilation;
212 }
213
allocateDeviceMemory(const test_wrapper::Compilation & compilation,const std::vector<uint32_t> & inputIndexes,const std::vector<uint32_t> & outputIndexes)214 std::pair<int, test_wrapper::Memory> allocateDeviceMemory(
215 const test_wrapper::Compilation& compilation, const std::vector<uint32_t>& inputIndexes,
216 const std::vector<uint32_t>& outputIndexes) {
217 const auto* annCompilation = compilation.getHandle();
218 ANeuralNetworksMemoryDesc* desc = nullptr;
219 EXPECT_EQ(ANeuralNetworksMemoryDesc_create(&desc), ANEURALNETWORKS_NO_ERROR);
220 for (uint32_t index : inputIndexes) {
221 EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(desc, annCompilation, index, 1.0f),
222 ANEURALNETWORKS_NO_ERROR);
223 }
224 for (uint32_t index : outputIndexes) {
225 EXPECT_EQ(ANeuralNetworksMemoryDesc_addOutputRole(desc, annCompilation, index, 1.0f),
226 ANEURALNETWORKS_NO_ERROR);
227 }
228 EXPECT_EQ(ANeuralNetworksMemoryDesc_finish(desc), ANEURALNETWORKS_NO_ERROR);
229
230 ANeuralNetworksMemory* memory;
231 int n = ANeuralNetworksMemory_createFromDesc(desc, &memory);
232 ANeuralNetworksMemoryDesc_free(desc);
233 return {n, test_wrapper::Memory(memory)};
234 }
235
236 static const test_wrapper::Model kModel;
237 };
238
239 const test_wrapper::Model MemoryDomainTestBase::kModel = createTestModel();
240
241 // Test memory domain with the following parameters
242 // - If true, use a V1_2 driver, otherwise, use the latest version;
243 // - If true, compile with explicit device list, otherwise, compile in the default way;
244 // - The return of the allocate function.
245 using MemoryDomainTestParam = std::tuple<bool, bool, AllocateReturn>;
246
247 class MemoryDomainTest : public MemoryDomainTestBase,
248 public ::testing::WithParamInterface<MemoryDomainTestParam> {
249 protected:
250 // If kUseV1_2Driver, allocateReturn must be AllocateReturn::NOT_SUPPORTED.
createAndRegisterDriver(const char * name,std::set<OperationType> supportedOperations,AllocateReturn allocateReturn)251 void createAndRegisterDriver(const char* name, std::set<OperationType> supportedOperations,
252 AllocateReturn allocateReturn) {
253 sp<V1_0::IDevice> driver;
254 if (kUseV1_2Driver) {
255 CHECK(allocateReturn == AllocateReturn::NOT_SUPPORTED);
256 const sp<TestDriverLatest> testDriver =
257 new TestDriverLatest(name, supportedOperations, AllocateReturn::NOT_SUPPORTED);
258 driver = new V1_2::ADevice(testDriver);
259 } else {
260 driver = new TestDriverLatest(name, std::move(supportedOperations), allocateReturn);
261 }
262 DeviceManager::get()->forTest_registerDevice(name, driver);
263 }
264
265 // If not kCompileWithExplicitDeviceList, the input argument "deviceNames" is ignored.
createCompilation(const std::vector<std::string> & deviceNames)266 test_wrapper::Compilation createCompilation(const std::vector<std::string>& deviceNames) {
267 if (kCompileWithExplicitDeviceList) {
268 return MemoryDomainTestBase::createCompilation(deviceNames);
269 } else {
270 return MemoryDomainTestBase::createCompilation({});
271 }
272 }
273
274 const bool kUseV1_2Driver = std::get<0>(GetParam());
275 const bool kCompileWithExplicitDeviceList = std::get<1>(GetParam());
276 const AllocateReturn kAllocateReturn = std::get<2>(GetParam());
277 };
278
279 // Test device memory allocation on a compilation with only a single partition.
TEST_P(MemoryDomainTest,SinglePartition)280 TEST_P(MemoryDomainTest, SinglePartition) {
281 createAndRegisterDriver("test_driver",
282 {OperationType::ADD, OperationType::SUB, OperationType::MUL},
283 kAllocateReturn);
284 auto compilation = createCompilation({"test_driver"});
285 ASSERT_NE(compilation.getHandle(), nullptr);
286
287 auto [n, memory] = allocateDeviceMemory(compilation, {0}, {0});
288 if (kAllocateReturn == AllocateReturn::OK) {
289 // The memory should be backed by the IBuffer returned from the driver.
290 ASSERT_EQ(n, ANEURALNETWORKS_NO_ERROR);
291 const Memory* m = reinterpret_cast<const Memory*>(memory.get());
292 ASSERT_NE(m, nullptr);
293 EXPECT_NE(m->getIBuffer(), nullptr);
294 } else {
295 if (kCompileWithExplicitDeviceList) {
296 // Should not fallback when the compiled with explicit device list.
297 ASSERT_EQ(n, ANEURALNETWORKS_OP_FAILED);
298 } else {
299 // The memory should fallback to ashmem or blob ahwb based on the driver version.
300 ASSERT_EQ(n, ANEURALNETWORKS_NO_ERROR);
301 const Memory* m = reinterpret_cast<const Memory*>(memory.get());
302 ASSERT_NE(m, nullptr);
303 EXPECT_EQ(m->getIBuffer(), nullptr);
304 const auto& hidlMemory = m->getHidlMemory();
305 EXPECT_TRUE(hidlMemory.valid());
306 if (kUseV1_2Driver) {
307 EXPECT_EQ(hidlMemory.name(), "ashmem");
308 } else {
309 EXPECT_EQ(hidlMemory.name(), "hardware_buffer_blob");
310 }
311 }
312 }
313 }
314
315 // Test device memory allocation on a compilation with multiple partitions.
TEST_P(MemoryDomainTest,MultiplePartitions)316 TEST_P(MemoryDomainTest, MultiplePartitions) {
317 createAndRegisterDriver("test_driver_add", {OperationType::ADD}, kAllocateReturn);
318 createAndRegisterDriver("test_driver_sub", {OperationType::SUB}, kAllocateReturn);
319 createAndRegisterDriver("test_driver_mul", {OperationType::MUL}, kAllocateReturn);
320 auto compilation = createCompilation({"test_driver_add", "test_driver_sub", "test_driver_mul"});
321 ASSERT_NE(compilation.getHandle(), nullptr);
322
323 {
324 // input0 is only used in one single partition.
325 auto [n, memory] = allocateDeviceMemory(compilation, {0}, {});
326 if (kAllocateReturn == AllocateReturn::OK) {
327 // The memory should be backed by the IBuffer returned from the driver.
328 ASSERT_EQ(n, ANEURALNETWORKS_NO_ERROR);
329 const Memory* m = reinterpret_cast<const Memory*>(memory.get());
330 ASSERT_NE(m, nullptr);
331 EXPECT_NE(m->getIBuffer(), nullptr);
332 } else {
333 if (kCompileWithExplicitDeviceList) {
334 // Should not fallback when the compiled with explicit device list.
335 ASSERT_EQ(n, ANEURALNETWORKS_OP_FAILED);
336 } else {
337 // The memory should fallback to ashmem or blob ahwb based on the driver version.
338 ASSERT_EQ(n, ANEURALNETWORKS_NO_ERROR);
339 const Memory* m = reinterpret_cast<const Memory*>(memory.get());
340 ASSERT_NE(m, nullptr);
341 EXPECT_EQ(m->getIBuffer(), nullptr);
342 const auto& hidlMemory = m->getHidlMemory();
343 EXPECT_TRUE(hidlMemory.valid());
344 if (kUseV1_2Driver) {
345 EXPECT_EQ(hidlMemory.name(), "ashmem");
346 } else {
347 EXPECT_EQ(hidlMemory.name(), "hardware_buffer_blob");
348 }
349 }
350 }
351 }
352
353 {
354 // input1 is shared by two partitions with different drivers, so the runtime will not
355 // attempt to allocate on device.
356 auto [n, memory] = allocateDeviceMemory(compilation, {1}, {});
357 if (kCompileWithExplicitDeviceList) {
358 // Should not fallback when the compiled with explicit device list.
359 ASSERT_EQ(n, ANEURALNETWORKS_OP_FAILED);
360 } else {
361 // The memory should fallback to ashmem or blob ahwb based on the driver version.
362 ASSERT_EQ(n, ANEURALNETWORKS_NO_ERROR);
363 const Memory* m = reinterpret_cast<const Memory*>(memory.get());
364 ASSERT_NE(m, nullptr);
365 EXPECT_EQ(m->getIBuffer(), nullptr);
366 const auto& hidlMemory = m->getHidlMemory();
367 EXPECT_TRUE(hidlMemory.valid());
368 if (kUseV1_2Driver) {
369 EXPECT_EQ(hidlMemory.name(), "ashmem");
370 } else {
371 EXPECT_EQ(hidlMemory.name(), "hardware_buffer_blob");
372 }
373 }
374 }
375
376 {
377 // output0 is shared by two partitions with different drivers, so the runtime will not
378 // attempt to allocate on device.
379 auto [n, memory] = allocateDeviceMemory(compilation, {}, {0});
380 if (kCompileWithExplicitDeviceList) {
381 // Should not fallback when the compiled with explicit device list.
382 ASSERT_EQ(n, ANEURALNETWORKS_OP_FAILED);
383 } else {
384 // The memory should fallback to ashmem or blob ahwb based on the driver version.
385 ASSERT_EQ(n, ANEURALNETWORKS_NO_ERROR);
386 const Memory* m = reinterpret_cast<const Memory*>(memory.get());
387 ASSERT_NE(m, nullptr);
388 EXPECT_EQ(m->getIBuffer(), nullptr);
389 const auto& hidlMemory = m->getHidlMemory();
390 EXPECT_TRUE(hidlMemory.valid());
391 if (kUseV1_2Driver) {
392 EXPECT_EQ(hidlMemory.name(), "ashmem");
393 } else {
394 EXPECT_EQ(hidlMemory.name(), "hardware_buffer_blob");
395 }
396 }
397 }
398 }
399
400 // Test device memory allocation with dynamic shape.
TEST_P(MemoryDomainTest,DynamicShape)401 TEST_P(MemoryDomainTest, DynamicShape) {
402 createAndRegisterDriver("test_driver",
403 {OperationType::ADD, OperationType::SUB, OperationType::MUL},
404 kAllocateReturn);
405 auto compilation = createCompilation({"test_driver"});
406 ASSERT_NE(compilation.getHandle(), nullptr);
407
408 auto [n, memory] = allocateDeviceMemory(compilation, {}, {1});
409 if (kAllocateReturn == AllocateReturn::OK) {
410 // The memory should be backed by the IBuffer returned from the driver.
411 ASSERT_EQ(n, ANEURALNETWORKS_NO_ERROR);
412 const Memory* m = reinterpret_cast<const Memory*>(memory.get());
413 ASSERT_NE(m, nullptr);
414 EXPECT_NE(m->getIBuffer(), nullptr);
415 } else {
416 // We do not fallback in the case of dynamic shape.
417 ASSERT_EQ(n, ANEURALNETWORKS_OP_FAILED);
418 }
419 }
420
421 static const auto kAllocateReturnChoices =
422 testing::Values(AllocateReturn::OK, AllocateReturn::BAD_TOKEN, AllocateReturn::BAD_IBUFFER,
423 AllocateReturn::BAD_STATUS, AllocateReturn::NOT_SUPPORTED);
424
425 INSTANTIATE_TEST_CASE_P(DeviceVersionLatest, MemoryDomainTest,
426 testing::Combine(testing::Values(false), testing::Bool(),
427 kAllocateReturnChoices));
428 INSTANTIATE_TEST_CASE_P(DeviceVersionV1_2, MemoryDomainTest,
429 testing::Combine(testing::Values(true), testing::Bool(),
430 testing::Values(AllocateReturn::NOT_SUPPORTED)));
431
432 class MemoryDomainCopyTest : public MemoryDomainTestBase {};
433
TEST_F(MemoryDomainCopyTest,MemoryCopyTest)434 TEST_F(MemoryDomainCopyTest, MemoryCopyTest) {
435 sp<sample_driver::SampleDriverFull> driver(new sample_driver::SampleDriverFull(
436 "test_driver", {.execTime = 0.1f, .powerUsage = 0.1f}));
437 DeviceManager::get()->forTest_registerDevice("test_driver", driver);
438 auto compilation = createCompilation({"test_driver"});
439 ASSERT_NE(compilation.getHandle(), nullptr);
440
441 // Allocate ashmem.
442 const float initValue1 = 3.14f, initValue2 = 2.72f;
443 auto ashmem1 = TestAshmem::createFrom(&initValue1, sizeof(float));
444 auto ashmem2 = TestAshmem::createFrom(&initValue2, sizeof(float));
445 ASSERT_NE(ashmem1, nullptr);
446 ASSERT_NE(ashmem2, nullptr);
447
448 // Allocate device memories.
449 auto [n1, memory1] = allocateDeviceMemory(compilation, {0}, {});
450 auto [n2, memory2] = allocateDeviceMemory(compilation, {0}, {});
451 ASSERT_EQ(n1, ANEURALNETWORKS_NO_ERROR);
452 ASSERT_EQ(n2, ANEURALNETWORKS_NO_ERROR);
453
454 // Test memory copying: ashmem1 -> memory1 -> memory2 -> ashmem2
455 ASSERT_EQ(ANeuralNetworksMemory_copy(ashmem1->get()->get(), memory1.get()),
456 ANEURALNETWORKS_NO_ERROR);
457 ASSERT_EQ(ANeuralNetworksMemory_copy(memory1.get(), memory2.get()), ANEURALNETWORKS_NO_ERROR);
458 ASSERT_EQ(ANeuralNetworksMemory_copy(memory2.get(), ashmem2->get()->get()),
459 ANEURALNETWORKS_NO_ERROR);
460
461 EXPECT_EQ(ashmem2->dataAs<float>()[0], initValue1);
462 }
463
464 } // namespace
465