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