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 // Provides C++ classes to more easily use the Neural Networks API.
18 // TODO(b/117845862): this should be auto generated from NeuralNetworksWrapper.h.
19 
20 #ifndef ANDROID_FRAMEWORKS_ML_NN_RUNTIME_TEST_TEST_NEURAL_NETWORKS_WRAPPER_H
21 #define ANDROID_FRAMEWORKS_ML_NN_RUNTIME_TEST_TEST_NEURAL_NETWORKS_WRAPPER_H
22 
23 #include <math.h>
24 
25 #include <algorithm>
26 #include <memory>
27 #include <optional>
28 #include <string>
29 #include <utility>
30 #include <vector>
31 
32 #include "NeuralNetworks.h"
33 #include "NeuralNetworksWrapper.h"
34 #include "NeuralNetworksWrapperExtensions.h"
35 
36 namespace android {
37 namespace nn {
38 namespace test_wrapper {
39 
40 using wrapper::Event;
41 using wrapper::ExecutePreference;
42 using wrapper::ExecutePriority;
43 using wrapper::ExtensionModel;
44 using wrapper::ExtensionOperandParams;
45 using wrapper::ExtensionOperandType;
46 using wrapper::OperandType;
47 using wrapper::Result;
48 using wrapper::SymmPerChannelQuantParams;
49 using wrapper::Type;
50 
51 class Memory {
52    public:
53     // Takes ownership of a ANeuralNetworksMemory
Memory(ANeuralNetworksMemory * memory)54     Memory(ANeuralNetworksMemory* memory) : mMemory(memory) {}
55 
Memory(size_t size,int protect,int fd,size_t offset)56     Memory(size_t size, int protect, int fd, size_t offset) {
57         mValid = ANeuralNetworksMemory_createFromFd(size, protect, fd, offset, &mMemory) ==
58                  ANEURALNETWORKS_NO_ERROR;
59     }
60 
Memory(AHardwareBuffer * buffer)61     Memory(AHardwareBuffer* buffer) {
62         mValid = ANeuralNetworksMemory_createFromAHardwareBuffer(buffer, &mMemory) ==
63                  ANEURALNETWORKS_NO_ERROR;
64     }
65 
~Memory()66     virtual ~Memory() { ANeuralNetworksMemory_free(mMemory); }
67 
68     // Disallow copy semantics to ensure the runtime object can only be freed
69     // once. Copy semantics could be enabled if some sort of reference counting
70     // or deep-copy system for runtime objects is added later.
71     Memory(const Memory&) = delete;
72     Memory& operator=(const Memory&) = delete;
73 
74     // Move semantics to remove access to the runtime object from the wrapper
75     // object that is being moved. This ensures the runtime object will be
76     // freed only once.
Memory(Memory && other)77     Memory(Memory&& other) { *this = std::move(other); }
78     Memory& operator=(Memory&& other) {
79         if (this != &other) {
80             ANeuralNetworksMemory_free(mMemory);
81             mMemory = other.mMemory;
82             mValid = other.mValid;
83             other.mMemory = nullptr;
84             other.mValid = false;
85         }
86         return *this;
87     }
88 
get()89     ANeuralNetworksMemory* get() const { return mMemory; }
isValid()90     bool isValid() const { return mValid; }
91 
92    private:
93     ANeuralNetworksMemory* mMemory = nullptr;
94     bool mValid = true;
95 };
96 
97 class Model {
98    public:
Model()99     Model() {
100         // TODO handle the value returned by this call
101         ANeuralNetworksModel_create(&mModel);
102     }
~Model()103     ~Model() { ANeuralNetworksModel_free(mModel); }
104 
105     // Disallow copy semantics to ensure the runtime object can only be freed
106     // once. Copy semantics could be enabled if some sort of reference counting
107     // or deep-copy system for runtime objects is added later.
108     Model(const Model&) = delete;
109     Model& operator=(const Model&) = delete;
110 
111     // Move semantics to remove access to the runtime object from the wrapper
112     // object that is being moved. This ensures the runtime object will be
113     // freed only once.
Model(Model && other)114     Model(Model&& other) { *this = std::move(other); }
115     Model& operator=(Model&& other) {
116         if (this != &other) {
117             ANeuralNetworksModel_free(mModel);
118             mModel = other.mModel;
119             mNextOperandId = other.mNextOperandId;
120             mValid = other.mValid;
121             mRelaxed = other.mRelaxed;
122             mFinished = other.mFinished;
123             other.mModel = nullptr;
124             other.mNextOperandId = 0;
125             other.mValid = false;
126             other.mRelaxed = false;
127             other.mFinished = false;
128         }
129         return *this;
130     }
131 
finish()132     Result finish() {
133         if (mValid) {
134             auto result = static_cast<Result>(ANeuralNetworksModel_finish(mModel));
135             if (result != Result::NO_ERROR) {
136                 mValid = false;
137             }
138             mFinished = true;
139             return result;
140         } else {
141             return Result::BAD_STATE;
142         }
143     }
144 
addOperand(const OperandType * type)145     uint32_t addOperand(const OperandType* type) {
146         if (ANeuralNetworksModel_addOperand(mModel, &(type->operandType)) !=
147             ANEURALNETWORKS_NO_ERROR) {
148             mValid = false;
149         }
150         if (type->channelQuant) {
151             if (ANeuralNetworksModel_setOperandSymmPerChannelQuantParams(
152                         mModel, mNextOperandId, &type->channelQuant.value().params) !=
153                 ANEURALNETWORKS_NO_ERROR) {
154                 mValid = false;
155             }
156         }
157         return mNextOperandId++;
158     }
159 
160     template <typename T>
addConstantOperand(const OperandType * type,const T & value)161     uint32_t addConstantOperand(const OperandType* type, const T& value) {
162         static_assert(sizeof(T) <= ANEURALNETWORKS_MAX_SIZE_OF_IMMEDIATELY_COPIED_VALUES,
163                       "Values larger than ANEURALNETWORKS_MAX_SIZE_OF_IMMEDIATELY_COPIED_VALUES "
164                       "not supported");
165         uint32_t index = addOperand(type);
166         setOperandValue(index, &value);
167         return index;
168     }
169 
addModelOperand(const Model * value)170     uint32_t addModelOperand(const Model* value) {
171         OperandType operandType(Type::MODEL, {});
172         uint32_t operand = addOperand(&operandType);
173         setOperandValueFromModel(operand, value);
174         return operand;
175     }
176 
setOperandValue(uint32_t index,const void * buffer,size_t length)177     void setOperandValue(uint32_t index, const void* buffer, size_t length) {
178         if (ANeuralNetworksModel_setOperandValue(mModel, index, buffer, length) !=
179             ANEURALNETWORKS_NO_ERROR) {
180             mValid = false;
181         }
182     }
183 
184     template <typename T>
setOperandValue(uint32_t index,const T * value)185     void setOperandValue(uint32_t index, const T* value) {
186         static_assert(!std::is_pointer<T>(), "No operand may have a pointer as its value");
187         return setOperandValue(index, value, sizeof(T));
188     }
189 
setOperandValueFromMemory(uint32_t index,const Memory * memory,uint32_t offset,size_t length)190     void setOperandValueFromMemory(uint32_t index, const Memory* memory, uint32_t offset,
191                                    size_t length) {
192         if (ANeuralNetworksModel_setOperandValueFromMemory(mModel, index, memory->get(), offset,
193                                                            length) != ANEURALNETWORKS_NO_ERROR) {
194             mValid = false;
195         }
196     }
197 
setOperandValueFromModel(uint32_t index,const Model * value)198     void setOperandValueFromModel(uint32_t index, const Model* value) {
199         if (ANeuralNetworksModel_setOperandValueFromModel(mModel, index, value->mModel) !=
200             ANEURALNETWORKS_NO_ERROR) {
201             mValid = false;
202         }
203     }
204 
addOperation(ANeuralNetworksOperationType type,const std::vector<uint32_t> & inputs,const std::vector<uint32_t> & outputs)205     void addOperation(ANeuralNetworksOperationType type, const std::vector<uint32_t>& inputs,
206                       const std::vector<uint32_t>& outputs) {
207         if (ANeuralNetworksModel_addOperation(mModel, type, static_cast<uint32_t>(inputs.size()),
208                                               inputs.data(), static_cast<uint32_t>(outputs.size()),
209                                               outputs.data()) != ANEURALNETWORKS_NO_ERROR) {
210             mValid = false;
211         }
212     }
identifyInputsAndOutputs(const std::vector<uint32_t> & inputs,const std::vector<uint32_t> & outputs)213     void identifyInputsAndOutputs(const std::vector<uint32_t>& inputs,
214                                   const std::vector<uint32_t>& outputs) {
215         if (ANeuralNetworksModel_identifyInputsAndOutputs(
216                     mModel, static_cast<uint32_t>(inputs.size()), inputs.data(),
217                     static_cast<uint32_t>(outputs.size()),
218                     outputs.data()) != ANEURALNETWORKS_NO_ERROR) {
219             mValid = false;
220         }
221     }
222 
relaxComputationFloat32toFloat16(bool isRelax)223     void relaxComputationFloat32toFloat16(bool isRelax) {
224         if (ANeuralNetworksModel_relaxComputationFloat32toFloat16(mModel, isRelax) ==
225             ANEURALNETWORKS_NO_ERROR) {
226             mRelaxed = isRelax;
227         }
228     }
229 
getHandle()230     ANeuralNetworksModel* getHandle() const { return mModel; }
isValid()231     bool isValid() const { return mValid; }
isRelaxed()232     bool isRelaxed() const { return mRelaxed; }
isFinished()233     bool isFinished() const { return mFinished; }
234 
235    protected:
236     ANeuralNetworksModel* mModel = nullptr;
237     // We keep track of the operand ID as a convenience to the caller.
238     uint32_t mNextOperandId = 0;
239     bool mValid = true;
240     bool mRelaxed = false;
241     bool mFinished = false;
242 };
243 
244 class Compilation {
245    public:
246     // On success, createForDevice(s) will return Result::NO_ERROR and the created compilation;
247     // otherwise, it will return the error code and Compilation object wrapping a nullptr handle.
createForDevice(const Model * model,const ANeuralNetworksDevice * device)248     static std::pair<Result, Compilation> createForDevice(const Model* model,
249                                                           const ANeuralNetworksDevice* device) {
250         return createForDevices(model, {device});
251     }
createForDevices(const Model * model,const std::vector<const ANeuralNetworksDevice * > & devices)252     static std::pair<Result, Compilation> createForDevices(
253             const Model* model, const std::vector<const ANeuralNetworksDevice*>& devices) {
254         ANeuralNetworksCompilation* compilation = nullptr;
255         const Result result = static_cast<Result>(ANeuralNetworksCompilation_createForDevices(
256                 model->getHandle(), devices.empty() ? nullptr : devices.data(), devices.size(),
257                 &compilation));
258         return {result, Compilation(compilation)};
259     }
260 
Compilation(const Model * model)261     Compilation(const Model* model) {
262         int result = ANeuralNetworksCompilation_create(model->getHandle(), &mCompilation);
263         if (result != 0) {
264             // TODO Handle the error
265         }
266     }
267 
Compilation()268     Compilation() {}
269 
~Compilation()270     ~Compilation() { ANeuralNetworksCompilation_free(mCompilation); }
271 
272     // Disallow copy semantics to ensure the runtime object can only be freed
273     // once. Copy semantics could be enabled if some sort of reference counting
274     // or deep-copy system for runtime objects is added later.
275     Compilation(const Compilation&) = delete;
276     Compilation& operator=(const Compilation&) = delete;
277 
278     // Move semantics to remove access to the runtime object from the wrapper
279     // object that is being moved. This ensures the runtime object will be
280     // freed only once.
Compilation(Compilation && other)281     Compilation(Compilation&& other) { *this = std::move(other); }
282     Compilation& operator=(Compilation&& other) {
283         if (this != &other) {
284             ANeuralNetworksCompilation_free(mCompilation);
285             mCompilation = other.mCompilation;
286             other.mCompilation = nullptr;
287         }
288         return *this;
289     }
290 
setPreference(ExecutePreference preference)291     Result setPreference(ExecutePreference preference) {
292         return static_cast<Result>(ANeuralNetworksCompilation_setPreference(
293                 mCompilation, static_cast<int32_t>(preference)));
294     }
295 
setPriority(ExecutePriority priority)296     Result setPriority(ExecutePriority priority) {
297         return static_cast<Result>(ANeuralNetworksCompilation_setPriority(
298                 mCompilation, static_cast<int32_t>(priority)));
299     }
300 
setCaching(const std::string & cacheDir,const std::vector<uint8_t> & token)301     Result setCaching(const std::string& cacheDir, const std::vector<uint8_t>& token) {
302         if (token.size() != ANEURALNETWORKS_BYTE_SIZE_OF_CACHE_TOKEN) {
303             return Result::BAD_DATA;
304         }
305         return static_cast<Result>(ANeuralNetworksCompilation_setCaching(
306                 mCompilation, cacheDir.c_str(), token.data()));
307     }
308 
finish()309     Result finish() { return static_cast<Result>(ANeuralNetworksCompilation_finish(mCompilation)); }
310 
getHandle()311     ANeuralNetworksCompilation* getHandle() const { return mCompilation; }
312 
313    protected:
314     // Takes the ownership of ANeuralNetworksCompilation.
Compilation(ANeuralNetworksCompilation * compilation)315     Compilation(ANeuralNetworksCompilation* compilation) : mCompilation(compilation) {}
316 
317     ANeuralNetworksCompilation* mCompilation = nullptr;
318 };
319 
320 class Execution {
321    public:
Execution(const Compilation * compilation)322     Execution(const Compilation* compilation) : mCompilation(compilation->getHandle()) {
323         int result = ANeuralNetworksExecution_create(compilation->getHandle(), &mExecution);
324         if (result != 0) {
325             // TODO Handle the error
326         }
327     }
328 
~Execution()329     ~Execution() { ANeuralNetworksExecution_free(mExecution); }
330 
331     // Disallow copy semantics to ensure the runtime object can only be freed
332     // once. Copy semantics could be enabled if some sort of reference counting
333     // or deep-copy system for runtime objects is added later.
334     Execution(const Execution&) = delete;
335     Execution& operator=(const Execution&) = delete;
336 
337     // Move semantics to remove access to the runtime object from the wrapper
338     // object that is being moved. This ensures the runtime object will be
339     // freed only once.
Execution(Execution && other)340     Execution(Execution&& other) { *this = std::move(other); }
341     Execution& operator=(Execution&& other) {
342         if (this != &other) {
343             ANeuralNetworksExecution_free(mExecution);
344             mCompilation = other.mCompilation;
345             other.mCompilation = nullptr;
346             mExecution = other.mExecution;
347             other.mExecution = nullptr;
348         }
349         return *this;
350     }
351 
352     Result setInput(uint32_t index, const void* buffer, size_t length,
353                     const ANeuralNetworksOperandType* type = nullptr) {
354         return static_cast<Result>(
355                 ANeuralNetworksExecution_setInput(mExecution, index, type, buffer, length));
356     }
357 
358     template <typename T>
359     Result setInput(uint32_t index, const T* value,
360                     const ANeuralNetworksOperandType* type = nullptr) {
361         static_assert(!std::is_pointer<T>(), "No operand may have a pointer as its value");
362         return setInput(index, value, sizeof(T), type);
363     }
364 
365     Result setInputFromMemory(uint32_t index, const Memory* memory, uint32_t offset,
366                               uint32_t length, const ANeuralNetworksOperandType* type = nullptr) {
367         return static_cast<Result>(ANeuralNetworksExecution_setInputFromMemory(
368                 mExecution, index, type, memory->get(), offset, length));
369     }
370 
371     Result setOutput(uint32_t index, void* buffer, size_t length,
372                      const ANeuralNetworksOperandType* type = nullptr) {
373         return static_cast<Result>(
374                 ANeuralNetworksExecution_setOutput(mExecution, index, type, buffer, length));
375     }
376 
377     template <typename T>
378     Result setOutput(uint32_t index, T* value, const ANeuralNetworksOperandType* type = nullptr) {
379         static_assert(!std::is_pointer<T>(), "No operand may have a pointer as its value");
380         return setOutput(index, value, sizeof(T), type);
381     }
382 
383     Result setOutputFromMemory(uint32_t index, const Memory* memory, uint32_t offset,
384                                uint32_t length, const ANeuralNetworksOperandType* type = nullptr) {
385         return static_cast<Result>(ANeuralNetworksExecution_setOutputFromMemory(
386                 mExecution, index, type, memory->get(), offset, length));
387     }
388 
setLoopTimeout(uint64_t duration)389     Result setLoopTimeout(uint64_t duration) {
390         return static_cast<Result>(ANeuralNetworksExecution_setLoopTimeout(mExecution, duration));
391     }
392 
startCompute(Event * event)393     Result startCompute(Event* event) {
394         ANeuralNetworksEvent* ev = nullptr;
395         Result result = static_cast<Result>(ANeuralNetworksExecution_startCompute(mExecution, &ev));
396         event->set(ev);
397         return result;
398     }
399 
startComputeWithDependencies(const std::vector<const Event * > & dependencies,uint64_t duration,Event * event)400     Result startComputeWithDependencies(const std::vector<const Event*>& dependencies,
401                                         uint64_t duration, Event* event) {
402         std::vector<const ANeuralNetworksEvent*> deps(dependencies.size());
403         std::transform(dependencies.begin(), dependencies.end(), deps.begin(),
404                        [](const Event* e) { return e->getHandle(); });
405         ANeuralNetworksEvent* ev = nullptr;
406         Result result = static_cast<Result>(ANeuralNetworksExecution_startComputeWithDependencies(
407                 mExecution, deps.data(), deps.size(), duration, &ev));
408         event->set(ev);
409         return result;
410     }
411 
compute()412     Result compute() {
413         switch (mComputeMode) {
414             case ComputeMode::SYNC: {
415                 return static_cast<Result>(ANeuralNetworksExecution_compute(mExecution));
416             }
417             case ComputeMode::ASYNC: {
418                 ANeuralNetworksEvent* event = nullptr;
419                 Result result = static_cast<Result>(
420                         ANeuralNetworksExecution_startCompute(mExecution, &event));
421                 if (result != Result::NO_ERROR) {
422                     return result;
423                 }
424                 // TODO how to manage the lifetime of events when multiple waiters is not
425                 // clear.
426                 result = static_cast<Result>(ANeuralNetworksEvent_wait(event));
427                 ANeuralNetworksEvent_free(event);
428                 return result;
429             }
430             case ComputeMode::BURST: {
431                 ANeuralNetworksBurst* burst = nullptr;
432                 Result result =
433                         static_cast<Result>(ANeuralNetworksBurst_create(mCompilation, &burst));
434                 if (result != Result::NO_ERROR) {
435                     return result;
436                 }
437                 result = static_cast<Result>(
438                         ANeuralNetworksExecution_burstCompute(mExecution, burst));
439                 ANeuralNetworksBurst_free(burst);
440                 return result;
441             }
442             case ComputeMode::FENCED: {
443                 ANeuralNetworksEvent* event = nullptr;
444                 Result result =
445                         static_cast<Result>(ANeuralNetworksExecution_startComputeWithDependencies(
446                                 mExecution, nullptr, 0, 0, &event));
447                 if (result != Result::NO_ERROR) {
448                     return result;
449                 }
450                 result = static_cast<Result>(ANeuralNetworksEvent_wait(event));
451                 ANeuralNetworksEvent_free(event);
452                 return result;
453             }
454         }
455         return Result::BAD_DATA;
456     }
457 
458     // By default, compute() uses the synchronous API. setComputeMode() can be
459     // used to change the behavior of compute() to either:
460     // - use the asynchronous API and then wait for computation to complete
461     // or
462     // - use the burst API
463     // Returns the previous ComputeMode.
464     enum class ComputeMode { SYNC, ASYNC, BURST, FENCED };
setComputeMode(ComputeMode mode)465     static ComputeMode setComputeMode(ComputeMode mode) {
466         ComputeMode oldComputeMode = mComputeMode;
467         mComputeMode = mode;
468         return oldComputeMode;
469     }
470 
getOutputOperandDimensions(uint32_t index,std::vector<uint32_t> * dimensions)471     Result getOutputOperandDimensions(uint32_t index, std::vector<uint32_t>* dimensions) {
472         uint32_t rank = 0;
473         Result result = static_cast<Result>(
474                 ANeuralNetworksExecution_getOutputOperandRank(mExecution, index, &rank));
475         dimensions->resize(rank);
476         if ((result != Result::NO_ERROR && result != Result::OUTPUT_INSUFFICIENT_SIZE) ||
477             rank == 0) {
478             return result;
479         }
480         result = static_cast<Result>(ANeuralNetworksExecution_getOutputOperandDimensions(
481                 mExecution, index, dimensions->data()));
482         return result;
483     }
484 
getHandle()485     ANeuralNetworksExecution* getHandle() { return mExecution; };
486 
487    private:
488     ANeuralNetworksCompilation* mCompilation = nullptr;
489     ANeuralNetworksExecution* mExecution = nullptr;
490 
491     // Initialized to ComputeMode::SYNC in TestNeuralNetworksWrapper.cpp.
492     static ComputeMode mComputeMode;
493 };
494 
495 }  // namespace test_wrapper
496 }  // namespace nn
497 }  // namespace android
498 
499 #endif  // ANDROID_FRAMEWORKS_ML_NN_RUNTIME_TEST_TEST_NEURAL_NETWORKS_WRAPPER_H
500