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