1 /*
2  * Copyright (C) 2017 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-base/scopeguard.h>
18 #include <gtest/gtest.h>
19 
20 #include "TestNeuralNetworksWrapper.h"
21 
22 #ifdef __ANDROID__
23 #include <android/hardware_buffer.h>
24 #endif  // __ANDROID__
25 
26 using namespace android::nn::test_wrapper;
27 
28 namespace {
29 
30 typedef float Matrix3x4[3][4];
31 typedef float Matrix4[4];
32 
33 const int32_t kNoActivation = ANEURALNETWORKS_FUSED_NONE;
34 
35 class TrivialTest : public ::testing::Test {
36    protected:
SetUp()37     virtual void SetUp() {}
38 
39 #if defined(__ANDROID__)
40     void testAddTwoWithHardwareBufferInput(uint64_t additionalAhwbUsage,
41                                            bool allowAllocationFailure);
42 #endif
43 
44     const Matrix3x4 matrix1 = {{1.f, 2.f, 3.f, 4.f}, {5.f, 6.f, 7.f, 8.f}, {9.f, 10.f, 11.f, 12.f}};
45     const Matrix3x4 matrix2 = {{100.f, 200.f, 300.f, 400.f},
46                                {500.f, 600.f, 700.f, 800.f},
47                                {900.f, 1000.f, 1100.f, 1200.f}};
48     const Matrix4 matrix2b = {100.f, 200.f, 300.f, 400.f};
49     const Matrix3x4 matrix3 = {
50             {20.f, 30.f, 40.f, 50.f}, {21.f, 22.f, 23.f, 24.f}, {31.f, 32.f, 33.f, 34.f}};
51     const Matrix3x4 expected2 = {{101.f, 202.f, 303.f, 404.f},
52                                  {505.f, 606.f, 707.f, 808.f},
53                                  {909.f, 1010.f, 1111.f, 1212.f}};
54     const Matrix3x4 expected2b = {{101.f, 202.f, 303.f, 404.f},
55                                   {105.f, 206.f, 307.f, 408.f},
56                                   {109.f, 210.f, 311.f, 412.f}};
57     const Matrix3x4 expected2c = {{100.f, 400.f, 900.f, 1600.f},
58                                   {500.f, 1200.f, 2100.f, 3200.f},
59                                   {900.f, 2000.f, 3300.f, 4800.f}};
60 
61     const Matrix3x4 expected3 = {{121.f, 232.f, 343.f, 454.f},
62                                  {526.f, 628.f, 730.f, 832.f},
63                                  {940.f, 1042.f, 1144.f, 1246.f}};
64     const Matrix3x4 expected3b = {
65             {22.f, 34.f, 46.f, 58.f}, {31.f, 34.f, 37.f, 40.f}, {49.f, 52.f, 55.f, 58.f}};
66 };
67 
68 // Create a model that can add two tensors using a one node graph.
CreateAddTwoTensorModel(Model * model)69 void CreateAddTwoTensorModel(Model* model) {
70     OperandType matrixType(Type::TENSOR_FLOAT32, {3, 4});
71     OperandType scalarType(Type::INT32, {});
72     auto a = model->addOperand(&matrixType);
73     auto b = model->addOperand(&matrixType);
74     auto c = model->addOperand(&matrixType);
75     auto d = model->addConstantOperand(&scalarType, kNoActivation);
76     model->addOperation(ANEURALNETWORKS_ADD, {a, b, d}, {c});
77     model->identifyInputsAndOutputs({a, b}, {c});
78     ASSERT_TRUE(model->isValid());
79     model->finish();
80 }
81 
82 // Create a model that can add three tensors using a two node graph,
83 // with one tensor set as part of the model.
CreateAddThreeTensorModel(Model * model,const Matrix3x4 bias)84 void CreateAddThreeTensorModel(Model* model, const Matrix3x4 bias) {
85     OperandType matrixType(Type::TENSOR_FLOAT32, {3, 4});
86     OperandType scalarType(Type::INT32, {});
87     auto a = model->addOperand(&matrixType);
88     auto b = model->addOperand(&matrixType);
89     auto c = model->addOperand(&matrixType);
90     auto d = model->addOperand(&matrixType);
91     auto e = model->addOperand(&matrixType);
92     auto f = model->addConstantOperand(&scalarType, kNoActivation);
93     model->setOperandValue(e, bias, sizeof(Matrix3x4));
94     model->addOperation(ANEURALNETWORKS_ADD, {a, c, f}, {b});
95     model->addOperation(ANEURALNETWORKS_ADD, {b, e, f}, {d});
96     model->identifyInputsAndOutputs({c, a}, {d});
97     ASSERT_TRUE(model->isValid());
98     model->finish();
99 }
100 
101 // Check that the values are the same. This works only if dealing with integer
102 // value, otherwise we should accept values that are similar if not exact.
CompareMatrices(const Matrix3x4 & expected,const Matrix3x4 & actual)103 int CompareMatrices(const Matrix3x4& expected, const Matrix3x4& actual) {
104     int errors = 0;
105     for (int i = 0; i < 3; i++) {
106         for (int j = 0; j < 4; j++) {
107             if (expected[i][j] != actual[i][j]) {
108                 printf("expected[%d][%d] != actual[%d][%d], %f != %f\n", i, j, i, j,
109                        static_cast<double>(expected[i][j]), static_cast<double>(actual[i][j]));
110                 errors++;
111             }
112         }
113     }
114     return errors;
115 }
116 
TEST_F(TrivialTest,AddTwo)117 TEST_F(TrivialTest, AddTwo) {
118     Model modelAdd2;
119     CreateAddTwoTensorModel(&modelAdd2);
120 
121     // Test the one node model.
122     Matrix3x4 actual;
123     memset(&actual, 0, sizeof(actual));
124     Compilation compilation(&modelAdd2);
125     compilation.finish();
126     Execution execution(&compilation);
127     ASSERT_EQ(execution.setInput(0, matrix1, sizeof(Matrix3x4)), Result::NO_ERROR);
128     ASSERT_EQ(execution.setInput(1, matrix2, sizeof(Matrix3x4)), Result::NO_ERROR);
129     ASSERT_EQ(execution.setOutput(0, actual, sizeof(Matrix3x4)), Result::NO_ERROR);
130     ASSERT_EQ(execution.compute(), Result::NO_ERROR);
131     ASSERT_EQ(CompareMatrices(expected2, actual), 0);
132 }
133 
134 // Hardware buffers are an Android concept, which aren't necessarily
135 // available on other platforms such as ChromeOS, which also build NNAPI.
136 #if defined(__ANDROID__)
testAddTwoWithHardwareBufferInput(uint64_t additionalAhwbUsage,bool allowAllocationFailure)137 void TrivialTest::testAddTwoWithHardwareBufferInput(uint64_t additionalAhwbUsage,
138                                                     bool allowAllocationFailure) {
139     Model modelAdd2;
140     CreateAddTwoTensorModel(&modelAdd2);
141 
142     const uint64_t cpuUsage =
143             AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
144     AHardwareBuffer_Desc desc{
145             .width = sizeof(matrix1),
146             .height = 1,
147             .layers = 1,
148             .format = AHARDWAREBUFFER_FORMAT_BLOB,
149             .usage = cpuUsage | additionalAhwbUsage,
150     };
151     AHardwareBuffer* matrix1Buffer = nullptr;
152     int err = AHardwareBuffer_allocate(&desc, &matrix1Buffer);
153     if (allowAllocationFailure && err != 0) {
154         GTEST_SKIP() << "Test skipped: AHardwareBuffer_allocate failed";
155     }
156     ASSERT_EQ(err, 0);
157     auto allocateGuard = android::base::make_scope_guard(
158             [matrix1Buffer]() { AHardwareBuffer_release(matrix1Buffer); });
159 
160     Memory matrix1Memory(matrix1Buffer);
161     ASSERT_TRUE(matrix1Memory.isValid());
162 
163     // Test the one node model.
164     Matrix3x4 actual;
165     memset(&actual, 0, sizeof(actual));
166     Compilation compilation(&modelAdd2);
167     compilation.finish();
168     Execution execution(&compilation);
169     ASSERT_EQ(execution.setInputFromMemory(0, &matrix1Memory, 0, sizeof(Matrix3x4)),
170               Result::NO_ERROR);
171     ASSERT_EQ(execution.setInput(1, matrix2, sizeof(Matrix3x4)), Result::NO_ERROR);
172     ASSERT_EQ(execution.setOutput(0, actual, sizeof(Matrix3x4)), Result::NO_ERROR);
173 
174     // Set the value for matrix1Buffer.
175     void* bufferPtr = nullptr;
176     ASSERT_EQ(AHardwareBuffer_lock(matrix1Buffer, cpuUsage, -1, NULL, &bufferPtr), 0);
177     memcpy((uint8_t*)bufferPtr, matrix1, sizeof(matrix1));
178     int synFenceFd = -1;
179     ASSERT_EQ(AHardwareBuffer_unlock(matrix1Buffer, &synFenceFd), 0);
180     if (synFenceFd > 0) {
181         // If valid sync fence is return by AHardwareBuffer_unlock, use
182         // ANeuralNetworksExecution_startComputeWithDependencies
183         ANeuralNetworksEvent* eventBufferUnlock;
184         ANeuralNetworksEvent* eventToSignal;
185         ASSERT_EQ(ANeuralNetworksEvent_createFromSyncFenceFd(synFenceFd, &eventBufferUnlock),
186                   ANEURALNETWORKS_NO_ERROR);
187         close(synFenceFd);
188         ANeuralNetworksExecution* executionHandle = execution.getHandle();
189         ASSERT_EQ(ANeuralNetworksExecution_startComputeWithDependencies(
190                           executionHandle, &eventBufferUnlock, 1, 0, &eventToSignal),
191                   ANEURALNETWORKS_NO_ERROR);
192         ASSERT_EQ(ANeuralNetworksEvent_wait(eventToSignal), ANEURALNETWORKS_NO_ERROR);
193         ANeuralNetworksEvent_free(eventBufferUnlock);
194         ANeuralNetworksEvent_free(eventToSignal);
195     } else {
196         ASSERT_EQ(execution.compute(), Result::NO_ERROR);
197     }
198 
199     ASSERT_EQ(CompareMatrices(expected2, actual), 0);
200 }
201 
TEST_F(TrivialTest,AddTwoWithHardwareBufferInput)202 TEST_F(TrivialTest, AddTwoWithHardwareBufferInput) {
203     testAddTwoWithHardwareBufferInput(/* no additional usage */ 0u,
204                                       /*allowAllocationFailure=*/false);
205 }
206 
TEST_F(TrivialTest,AddTwoWithHardwareBufferInputWithGPUUsage)207 TEST_F(TrivialTest, AddTwoWithHardwareBufferInputWithGPUUsage) {
208     testAddTwoWithHardwareBufferInput(AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER,
209                                       /*allowAllocationFailure=*/true);
210 }
211 #endif
212 
TEST_F(TrivialTest,AddThree)213 TEST_F(TrivialTest, AddThree) {
214     Model modelAdd3;
215     CreateAddThreeTensorModel(&modelAdd3, matrix3);
216 
217     // Test the three node model.
218     Matrix3x4 actual;
219     memset(&actual, 0, sizeof(actual));
220     Compilation compilation2(&modelAdd3);
221     compilation2.finish();
222     Execution execution2(&compilation2);
223     ASSERT_EQ(execution2.setInput(0, matrix1, sizeof(Matrix3x4)), Result::NO_ERROR);
224     ASSERT_EQ(execution2.setInput(1, matrix2, sizeof(Matrix3x4)), Result::NO_ERROR);
225     ASSERT_EQ(execution2.setOutput(0, actual, sizeof(Matrix3x4)), Result::NO_ERROR);
226     ASSERT_EQ(execution2.compute(), Result::NO_ERROR);
227     ASSERT_EQ(CompareMatrices(expected3, actual), 0);
228 
229     // Test it a second time to make sure the model is reusable.
230     memset(&actual, 0, sizeof(actual));
231     Compilation compilation3(&modelAdd3);
232     compilation3.finish();
233     Execution execution3(&compilation3);
234     ASSERT_EQ(execution3.setInput(0, matrix1, sizeof(Matrix3x4)), Result::NO_ERROR);
235     ASSERT_EQ(execution3.setInput(1, matrix1, sizeof(Matrix3x4)), Result::NO_ERROR);
236     ASSERT_EQ(execution3.setOutput(0, actual, sizeof(Matrix3x4)), Result::NO_ERROR);
237     ASSERT_EQ(execution3.compute(), Result::NO_ERROR);
238     ASSERT_EQ(CompareMatrices(expected3b, actual), 0);
239 }
240 
TEST_F(TrivialTest,FencedAddThree)241 TEST_F(TrivialTest, FencedAddThree) {
242     Model modelAdd3;
243     CreateAddThreeTensorModel(&modelAdd3, matrix3);
244     Compilation compilation(&modelAdd3);
245     compilation.finish();
246 
247     Matrix3x4 output1, output2;
248     memset(&output1, 0, sizeof(output1));
249     memset(&output2, 0, sizeof(output2));
250 
251     // Start the first execution
252     Execution execution1(&compilation);
253     ASSERT_EQ(execution1.setInput(0, matrix1, sizeof(Matrix3x4)), Result::NO_ERROR);
254     ASSERT_EQ(execution1.setInput(1, matrix2, sizeof(Matrix3x4)), Result::NO_ERROR);
255     ASSERT_EQ(execution1.setOutput(0, output1, sizeof(Matrix3x4)), Result::NO_ERROR);
256     ANeuralNetworksEvent* event1;
257     ANeuralNetworksExecution* execution1_handle = execution1.getHandle();
258     ASSERT_EQ(ANeuralNetworksExecution_startComputeWithDependencies(execution1_handle, nullptr, 0,
259                                                                     0, &event1),
260               ANEURALNETWORKS_NO_ERROR);
261 
262     // Start the second execution which will wait for the first one.
263     Execution execution2(&compilation);
264     ASSERT_EQ(execution2.setInput(0, matrix1, sizeof(Matrix3x4)), Result::NO_ERROR);
265     ASSERT_EQ(execution2.setInput(1, matrix1, sizeof(Matrix3x4)), Result::NO_ERROR);
266     ASSERT_EQ(execution2.setOutput(0, output2, sizeof(Matrix3x4)), Result::NO_ERROR);
267     ANeuralNetworksEvent* event2;
268     ANeuralNetworksExecution* execution2_handle = execution2.getHandle();
269     ASSERT_EQ(ANeuralNetworksExecution_startComputeWithDependencies(execution2_handle, &event1, 1,
270                                                                     0, &event2),
271               ANEURALNETWORKS_NO_ERROR);
272     // Wait for the second event.
273     ASSERT_EQ(ANeuralNetworksEvent_wait(event2), ANEURALNETWORKS_NO_ERROR);
274 
275     // Check the results for both executions.
276     ASSERT_EQ(CompareMatrices(expected3, output1), 0);
277     ASSERT_EQ(CompareMatrices(expected3b, output2), 0);
278 
279     // Free the event objects
280     ANeuralNetworksEvent_free(event1);
281     ANeuralNetworksEvent_free(event2);
282 }
283 
TEST_F(TrivialTest,BroadcastAddTwo)284 TEST_F(TrivialTest, BroadcastAddTwo) {
285     Model modelBroadcastAdd2;
286     OperandType scalarType(Type::INT32, {});
287     auto activation = modelBroadcastAdd2.addConstantOperand(&scalarType, kNoActivation);
288 
289     OperandType matrixType(Type::TENSOR_FLOAT32, {1, 1, 3, 4});
290     OperandType matrixType2(Type::TENSOR_FLOAT32, {4});
291 
292     auto a = modelBroadcastAdd2.addOperand(&matrixType);
293     auto b = modelBroadcastAdd2.addOperand(&matrixType2);
294     auto c = modelBroadcastAdd2.addOperand(&matrixType);
295     modelBroadcastAdd2.addOperation(ANEURALNETWORKS_ADD, {a, b, activation}, {c});
296     modelBroadcastAdd2.identifyInputsAndOutputs({a, b}, {c});
297     ASSERT_TRUE(modelBroadcastAdd2.isValid());
298     modelBroadcastAdd2.finish();
299 
300     // Test the one node model.
301     Matrix3x4 actual;
302     memset(&actual, 0, sizeof(actual));
303     Compilation compilation(&modelBroadcastAdd2);
304     compilation.finish();
305     Execution execution(&compilation);
306     ASSERT_EQ(execution.setInput(0, matrix1, sizeof(Matrix3x4)), Result::NO_ERROR);
307     ASSERT_EQ(execution.setInput(1, matrix2b, sizeof(Matrix4)), Result::NO_ERROR);
308     ASSERT_EQ(execution.setOutput(0, actual, sizeof(Matrix3x4)), Result::NO_ERROR);
309     ASSERT_EQ(execution.compute(), Result::NO_ERROR);
310     ASSERT_EQ(CompareMatrices(expected2b, actual), 0);
311 }
312 
TEST_F(TrivialTest,BroadcastMulTwo)313 TEST_F(TrivialTest, BroadcastMulTwo) {
314     Model modelBroadcastMul2;
315     OperandType scalarType(Type::INT32, {});
316     auto activation = modelBroadcastMul2.addConstantOperand(&scalarType, kNoActivation);
317 
318     OperandType matrixType(Type::TENSOR_FLOAT32, {1, 1, 3, 4});
319     OperandType matrixType2(Type::TENSOR_FLOAT32, {4});
320 
321     auto a = modelBroadcastMul2.addOperand(&matrixType);
322     auto b = modelBroadcastMul2.addOperand(&matrixType2);
323     auto c = modelBroadcastMul2.addOperand(&matrixType);
324     modelBroadcastMul2.addOperation(ANEURALNETWORKS_MUL, {a, b, activation}, {c});
325     modelBroadcastMul2.identifyInputsAndOutputs({a, b}, {c});
326     ASSERT_TRUE(modelBroadcastMul2.isValid());
327     modelBroadcastMul2.finish();
328 
329     // Test the one node model.
330     Matrix3x4 actual;
331     memset(&actual, 0, sizeof(actual));
332     Compilation compilation(&modelBroadcastMul2);
333     compilation.finish();
334     Execution execution(&compilation);
335     ASSERT_EQ(execution.setInput(0, matrix1, sizeof(Matrix3x4)), Result::NO_ERROR);
336     ASSERT_EQ(execution.setInput(1, matrix2b, sizeof(Matrix4)), Result::NO_ERROR);
337     ASSERT_EQ(execution.setOutput(0, actual, sizeof(Matrix3x4)), Result::NO_ERROR);
338     ASSERT_EQ(execution.compute(), Result::NO_ERROR);
339     ASSERT_EQ(CompareMatrices(expected2c, actual), 0);
340 }
341 
342 }  // end namespace
343