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