• 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 // Provides C++ classes to more easily use the Neural Networks API.
18 
19 #ifndef ANDROID_FRAMEWORKS_ML_NN_RUNTIME_NEURAL_NETWORKS_WRAPPER_H
20 #define ANDROID_FRAMEWORKS_ML_NN_RUNTIME_NEURAL_NETWORKS_WRAPPER_H
21 
22 #include <assert.h>
23 #include <math.h>
24 
25 #include <algorithm>
26 #include <optional>
27 #include <string>
28 #include <utility>
29 #include <vector>
30 
31 #ifdef NNTEST_SLTS
32 #include "SupportLibrary.h"
33 #else
34 #include "NeuralNetworks.h"
35 #endif
36 
37 namespace android {
38 namespace nn {
39 namespace wrapper {
40 
41 enum class Type {
42     FLOAT32 = ANEURALNETWORKS_FLOAT32,
43     INT32 = ANEURALNETWORKS_INT32,
44     UINT32 = ANEURALNETWORKS_UINT32,
45     TENSOR_FLOAT32 = ANEURALNETWORKS_TENSOR_FLOAT32,
46     TENSOR_INT32 = ANEURALNETWORKS_TENSOR_INT32,
47     TENSOR_QUANT8_ASYMM = ANEURALNETWORKS_TENSOR_QUANT8_ASYMM,
48     BOOL = ANEURALNETWORKS_BOOL,
49     TENSOR_QUANT16_SYMM = ANEURALNETWORKS_TENSOR_QUANT16_SYMM,
50     TENSOR_FLOAT16 = ANEURALNETWORKS_TENSOR_FLOAT16,
51     TENSOR_BOOL8 = ANEURALNETWORKS_TENSOR_BOOL8,
52     FLOAT16 = ANEURALNETWORKS_FLOAT16,
53     TENSOR_QUANT8_SYMM_PER_CHANNEL = ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL,
54     TENSOR_QUANT16_ASYMM = ANEURALNETWORKS_TENSOR_QUANT16_ASYMM,
55     TENSOR_QUANT8_SYMM = ANEURALNETWORKS_TENSOR_QUANT8_SYMM,
56     TENSOR_QUANT8_ASYMM_SIGNED = ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED,
57     MODEL = ANEURALNETWORKS_MODEL,
58 };
59 
60 enum class ExecutePreference {
61     PREFER_LOW_POWER = ANEURALNETWORKS_PREFER_LOW_POWER,
62     PREFER_FAST_SINGLE_ANSWER = ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER,
63     PREFER_SUSTAINED_SPEED = ANEURALNETWORKS_PREFER_SUSTAINED_SPEED
64 };
65 
66 enum class Duration {
67     ON_HARDWARE = ANEURALNETWORKS_DURATION_ON_HARDWARE,
68     IN_DRIVER = ANEURALNETWORKS_DURATION_IN_DRIVER,
69     FENCED_ON_HARDWARE = ANEURALNETWORKS_FENCED_DURATION_ON_HARDWARE,
70     FENCED_IN_DRIVER = ANEURALNETWORKS_FENCED_DURATION_IN_DRIVER,
71 };
72 
73 enum class ExecutePriority {
74     LOW = ANEURALNETWORKS_PRIORITY_LOW,
75     MEDIUM = ANEURALNETWORKS_PRIORITY_MEDIUM,
76     HIGH = ANEURALNETWORKS_PRIORITY_HIGH,
77     DEFAULT = ANEURALNETWORKS_PRIORITY_DEFAULT,
78 };
79 
80 enum class Result {
81     NO_ERROR = ANEURALNETWORKS_NO_ERROR,
82     OUT_OF_MEMORY = ANEURALNETWORKS_OUT_OF_MEMORY,
83     INCOMPLETE = ANEURALNETWORKS_INCOMPLETE,
84     UNEXPECTED_NULL = ANEURALNETWORKS_UNEXPECTED_NULL,
85     BAD_DATA = ANEURALNETWORKS_BAD_DATA,
86     OP_FAILED = ANEURALNETWORKS_OP_FAILED,
87     UNMAPPABLE = ANEURALNETWORKS_UNMAPPABLE,
88     BAD_STATE = ANEURALNETWORKS_BAD_STATE,
89     OUTPUT_INSUFFICIENT_SIZE = ANEURALNETWORKS_OUTPUT_INSUFFICIENT_SIZE,
90     UNAVAILABLE_DEVICE = ANEURALNETWORKS_UNAVAILABLE_DEVICE,
91     MISSED_DEADLINE_TRANSIENT = ANEURALNETWORKS_MISSED_DEADLINE_TRANSIENT,
92     MISSED_DEADLINE_PERSISTENT = ANEURALNETWORKS_MISSED_DEADLINE_PERSISTENT,
93 
94     // Functionality needed for this feature is not available on the current device.
95     FEATURE_LEVEL_TOO_LOW = 100001,
96 };
97 
98 struct SymmPerChannelQuantParams {
99     ANeuralNetworksSymmPerChannelQuantParams params;
100     std::vector<float> scales;
101 
SymmPerChannelQuantParamsSymmPerChannelQuantParams102     SymmPerChannelQuantParams(std::vector<float> scalesVec, uint32_t channelDim)
103         : scales(std::move(scalesVec)) {
104         params = {
105                 .channelDim = channelDim,
106                 .scaleCount = static_cast<uint32_t>(scales.size()),
107                 .scales = scales.size() > 0 ? scales.data() : nullptr,
108         };
109     }
110 
SymmPerChannelQuantParamsSymmPerChannelQuantParams111     SymmPerChannelQuantParams(const SymmPerChannelQuantParams& other)
112         : params(other.params), scales(other.scales) {
113         params.scales = scales.size() > 0 ? scales.data() : nullptr;
114     }
115 
116     SymmPerChannelQuantParams& operator=(const SymmPerChannelQuantParams& other) {
117         if (this != &other) {
118             params = other.params;
119             scales = other.scales;
120             params.scales = scales.size() > 0 ? scales.data() : nullptr;
121         }
122         return *this;
123     }
124 };
125 
126 struct OperandType {
127     ANeuralNetworksOperandType operandType;
128     std::vector<uint32_t> dimensions;
129     std::optional<SymmPerChannelQuantParams> channelQuant;
130 
OperandTypeOperandType131     OperandType(const OperandType& other)
132         : operandType(other.operandType),
133           dimensions(other.dimensions),
134           channelQuant(other.channelQuant) {
135         operandType.dimensions = dimensions.size() > 0 ? dimensions.data() : nullptr;
136     }
137 
138     OperandType& operator=(const OperandType& other) {
139         if (this != &other) {
140             operandType = other.operandType;
141             dimensions = other.dimensions;
142             channelQuant = other.channelQuant;
143             operandType.dimensions = dimensions.size() > 0 ? dimensions.data() : nullptr;
144         }
145         return *this;
146     }
147 
148     OperandType(Type type, std::vector<uint32_t> d, float scale = 0.0f, int32_t zeroPoint = 0)
dimensionsOperandType149         : dimensions(std::move(d)), channelQuant(std::nullopt) {
150         operandType = {
151                 .type = static_cast<int32_t>(type),
152                 .dimensionCount = static_cast<uint32_t>(dimensions.size()),
153                 .dimensions = dimensions.size() > 0 ? dimensions.data() : nullptr,
154                 .scale = scale,
155                 .zeroPoint = zeroPoint,
156         };
157     }
158 
OperandTypeOperandType159     OperandType(Type type, std::vector<uint32_t> data, SymmPerChannelQuantParams&& channelQuant)
160         : dimensions(std::move(data)), channelQuant(std::move(channelQuant)) {
161         assert(type == Type::TENSOR_QUANT8_SYMM_PER_CHANNEL);
162 
163         operandType = {
164                 .type = static_cast<int32_t>(type),
165                 .dimensionCount = static_cast<uint32_t>(dimensions.size()),
166                 .dimensions = dimensions.size() > 0 ? dimensions.data() : nullptr,
167                 .scale = 0.0f,
168                 .zeroPoint = 0,
169         };
170     }
171 
updateDimensionsOperandType172     void updateDimensions(std::vector<uint32_t> ndim) {
173         dimensions = ndim;
174         operandType.dimensions = dimensions.size() > 0 ? dimensions.data() : nullptr;
175     }
176 };
177 
178 #ifdef NNTEST_SLTS
179 #define NNAPI_CALL(apiCall) mNnApi->apiCall
180 #else
181 #define NNAPI_CALL(apiCall) apiCall
182 #endif
183 
184 class Memory {
185    public:
186 #ifdef NNTEST_SLTS
187     // Takes ownership of a ANeuralNetworksMemory
Memory(const NnApiSupportLibrary * nnapi,ANeuralNetworksMemory * memory)188     Memory(const NnApiSupportLibrary* nnapi, ANeuralNetworksMemory* memory)
189         : mNnApi(nnapi), mMemory(memory) {}
190 
Memory(const NnApiSupportLibrary * nnapi,size_t size,int protect,int fd,size_t offset)191     Memory(const NnApiSupportLibrary* nnapi, size_t size, int protect, int fd, size_t offset)
192         : mNnApi(nnapi) {
193 #else
194     Memory(size_t size, int protect, int fd, size_t offset) {
195 #endif
196         mValid = NNAPI_CALL(ANeuralNetworksMemory_createFromFd(
197                          size, protect, fd, offset, &mMemory)) == ANEURALNETWORKS_NO_ERROR;
198     }
199 
200 #ifdef NNTEST_SLTS
201     Memory(const NnApiSupportLibrary* nnapi, AHardwareBuffer* buffer) : mNnApi(nnapi) {
202 #else
203     Memory(AHardwareBuffer* buffer) {
204 #endif
205         mValid = NNAPI_CALL(ANeuralNetworksMemory_createFromAHardwareBuffer(buffer, &mMemory)) ==
206                  ANEURALNETWORKS_NO_ERROR;
207     }
208 
209     ~Memory() {
210         if (mMemory) {
211             NNAPI_CALL(ANeuralNetworksMemory_free(mMemory));
212         }
213     }
214 
215     // Disallow copy semantics to ensure the runtime object can only be freed
216     // once. Copy semantics could be enabled if some sort of reference counting
217     // or deep-copy system for runtime objects is added later.
218     Memory(const Memory&) = delete;
219     Memory& operator=(const Memory&) = delete;
220 
221     // Move semantics to remove access to the runtime object from the wrapper
222     // object that is being moved. This ensures the runtime object will be
223     // freed only once.
224     Memory(Memory&& other) { *this = std::move(other); }
225     Memory& operator=(Memory&& other) {
226         if (this != &other) {
227             if (mMemory) {
228                 NNAPI_CALL(ANeuralNetworksMemory_free(mMemory));
229             }
230             mMemory = other.mMemory;
231             mValid = other.mValid;
232             other.mMemory = nullptr;
233             other.mValid = false;
234         }
235         return *this;
236     }
237 
238     ANeuralNetworksMemory* get() const { return mMemory; }
239     bool isValid() const { return mValid; }
240 
241    private:
242 #ifdef NNTEST_SLTS
243     const NnApiSupportLibrary* mNnApi = nullptr;
244 #endif
245     ANeuralNetworksMemory* mMemory = nullptr;
246     bool mValid = true;
247 };
248 
249 class Model {
250    public:
251 #ifdef NNTEST_SLTS
252     Model(const NnApiSupportLibrary* nnapi) : mNnApi(nnapi) {
253 #else
254     Model() {
255 #endif
256         // TODO handle the value returned by this call
257         NNAPI_CALL(ANeuralNetworksModel_create(&mModel));
258     }
259     ~Model() {
260         if (mModel) {
261             NNAPI_CALL(ANeuralNetworksModel_free(mModel));
262         }
263     }
264 
265     // Disallow copy semantics to ensure the runtime object can only be freed
266     // once. Copy semantics could be enabled if some sort of reference counting
267     // or deep-copy system for runtime objects is added later.
268     Model(const Model&) = delete;
269     Model& operator=(const Model&) = delete;
270 
271     // Move semantics to remove access to the runtime object from the wrapper
272     // object that is being moved. This ensures the runtime object will be
273     // freed only once.
274     Model(Model&& other) { *this = std::move(other); }
275     Model& operator=(Model&& other) {
276         if (this != &other) {
277             if (mModel) {
278                 NNAPI_CALL(ANeuralNetworksModel_free(mModel));
279             }
280             mModel = other.mModel;
281             mNextOperandId = other.mNextOperandId;
282             mValid = other.mValid;
283             other.mModel = nullptr;
284             other.mNextOperandId = 0;
285             other.mValid = false;
286         }
287         return *this;
288     }
289 
290     Result finish() {
291         if (mValid) {
292             auto result = static_cast<Result>(NNAPI_CALL(ANeuralNetworksModel_finish(mModel)));
293             if (result != Result::NO_ERROR) {
294                 mValid = false;
295             }
296             return result;
297         } else {
298             return Result::BAD_STATE;
299         }
300     }
301 
302     uint32_t addOperand(const OperandType* type) {
303         if (NNAPI_CALL(ANeuralNetworksModel_addOperand(mModel, &(type->operandType))) !=
304             ANEURALNETWORKS_NO_ERROR) {
305             mValid = false;
306         }
307         if (type->channelQuant) {
308             if (NNAPI_CALL(ANeuralNetworksModel_setOperandSymmPerChannelQuantParams(
309                         mModel, mNextOperandId, &type->channelQuant.value().params)) !=
310                 ANEURALNETWORKS_NO_ERROR) {
311                 mValid = false;
312             }
313         }
314         return mNextOperandId++;
315     }
316 
317     void setOperandValue(uint32_t index, const void* buffer, size_t length) {
318         if (NNAPI_CALL(ANeuralNetworksModel_setOperandValue(mModel, index, buffer, length)) !=
319             ANEURALNETWORKS_NO_ERROR) {
320             mValid = false;
321         }
322     }
323 
324     void setOperandValueFromMemory(uint32_t index, const Memory* memory, uint32_t offset,
325                                    size_t length) {
326         if (NNAPI_CALL(ANeuralNetworksModel_setOperandValueFromMemory(
327                     mModel, index, memory->get(), offset, length)) != ANEURALNETWORKS_NO_ERROR) {
328             mValid = false;
329         }
330     }
331 
332     void addOperation(ANeuralNetworksOperationType type, const std::vector<uint32_t>& inputs,
333                       const std::vector<uint32_t>& outputs) {
334         if (NNAPI_CALL(ANeuralNetworksModel_addOperation(
335                     mModel, type, static_cast<uint32_t>(inputs.size()), inputs.data(),
336                     static_cast<uint32_t>(outputs.size()), outputs.data())) !=
337             ANEURALNETWORKS_NO_ERROR) {
338             mValid = false;
339         }
340     }
341     void identifyInputsAndOutputs(const std::vector<uint32_t>& inputs,
342                                   const std::vector<uint32_t>& outputs) {
343         if (NNAPI_CALL(ANeuralNetworksModel_identifyInputsAndOutputs(
344                     mModel, static_cast<uint32_t>(inputs.size()), inputs.data(),
345                     static_cast<uint32_t>(outputs.size()), outputs.data())) !=
346             ANEURALNETWORKS_NO_ERROR) {
347             mValid = false;
348         }
349     }
350 
351     void relaxComputationFloat32toFloat16(bool isRelax) {
352         if (NNAPI_CALL(ANeuralNetworksModel_relaxComputationFloat32toFloat16(mModel, isRelax)) ==
353             ANEURALNETWORKS_NO_ERROR) {
354             mRelaxed = isRelax;
355         }
356     }
357 
358     ANeuralNetworksModel* getHandle() const { return mModel; }
359     bool isValid() const { return mValid; }
360     bool isRelaxed() const { return mRelaxed; }
361 
362 #ifdef NNTEST_SLTS
363    private:
364     const NnApiSupportLibrary* mNnApi = nullptr;
365 #endif
366 
367    protected:
368     ANeuralNetworksModel* mModel = nullptr;
369     // We keep track of the operand ID as a convenience to the caller.
370     uint32_t mNextOperandId = 0;
371     bool mValid = true;
372     bool mRelaxed = false;
373 };
374 
375 class Event {
376    public:
377 #ifdef NNTEST_SLTS
378     Event(const NnApiSupportLibrary* nnapi) : mNnApi(nnapi) {}
379     Event(const NnApiSupportLibrary* nnapi, int syncFd) : mNnApi(nnapi) {
380 #else
381     Event() {}
382     Event(int syncFd) {
383 #endif
384         mValid = NNAPI_CALL(ANeuralNetworksEvent_createFromSyncFenceFd(syncFd, &mEvent)) ==
385                  ANEURALNETWORKS_NO_ERROR;
386     }
387 
388     ~Event() {
389         if (mEvent) {
390             NNAPI_CALL(ANeuralNetworksEvent_free(mEvent));
391         }
392     }
393 
394     // Disallow copy semantics to ensure the runtime object can only be freed
395     // once. Copy semantics could be enabled if some sort of reference counting
396     // or deep-copy system for runtime objects is added later.
397     Event(const Event&) = delete;
398     Event& operator=(const Event&) = delete;
399 
400     // Move semantics to remove access to the runtime object from the wrapper
401     // object that is being moved. This ensures the runtime object will be
402     // freed only once.
403     Event(Event&& other) { *this = std::move(other); }
404     Event& operator=(Event&& other) {
405         if (this != &other) {
406             if (mEvent) {
407                 NNAPI_CALL(ANeuralNetworksEvent_free(mEvent));
408             }
409 #ifdef NNTEST_SLTS
410             mNnApi = other.mNnApi;
411 #endif
412             mEvent = other.mEvent;
413             other.mEvent = nullptr;
414         }
415         return *this;
416     }
417 
418     Result wait() { return static_cast<Result>(NNAPI_CALL(ANeuralNetworksEvent_wait(mEvent))); }
419 
420     // Only for use by Execution
421     void set(ANeuralNetworksEvent* newEvent) {
422         if (mEvent) {
423             NNAPI_CALL(ANeuralNetworksEvent_free(mEvent));
424         }
425         mEvent = newEvent;
426     }
427 
428     // Only for use by Execution
429     ANeuralNetworksEvent* getHandle() const { return mEvent; }
430 
431     Result getSyncFenceFd(int* sync_fence_fd) {
432         return static_cast<Result>(
433                 NNAPI_CALL(ANeuralNetworksEvent_getSyncFenceFd(mEvent, sync_fence_fd)));
434     }
435 
436     bool isValid() const { return mValid; }
437 
438 #ifdef NNTEST_SLTS
439    private:
440     const NnApiSupportLibrary* mNnApi = nullptr;
441 #endif
442 
443    private:
444     bool mValid = true;
445     ANeuralNetworksEvent* mEvent = nullptr;
446 };
447 
448 class Compilation {
449    public:
450 #ifdef NNTEST_SLTS
451     // On success, createForDevice(s) will return Result::NO_ERROR and the created compilation;
452     // otherwise, it will return the error code and Compilation object wrapping a nullptr handle.
453     static std::pair<Result, Compilation> createForDevice(const NnApiSupportLibrary* nnapi,
454                                                           const Model* model,
455                                                           const ANeuralNetworksDevice* device) {
456         return createForDevices(nnapi, model, {device});
457     }
458     static std::pair<Result, Compilation> createForDevices(
459             const NnApiSupportLibrary* nnapi, const Model* model,
460             const std::vector<const ANeuralNetworksDevice*>& devices) {
461         ANeuralNetworksCompilation* compilation = nullptr;
462         const Result result =
463                 static_cast<Result>(nnapi->ANeuralNetworksCompilation_createForDevices(
464                         model->getHandle(), devices.empty() ? nullptr : devices.data(),
465                         devices.size(), &compilation));
466         return {result, Compilation(nnapi, compilation)};
467     }
468 #else
469     Compilation(const Model* model) {
470         int result =
471                 NNAPI_CALL(ANeuralNetworksCompilation_create(model->getHandle(), &mCompilation));
472         if (result != 0) {
473             // TODO Handle the error
474         }
475     }
476 #endif
477 
478     ~Compilation() { NNAPI_CALL(ANeuralNetworksCompilation_free(mCompilation)); }
479 
480     // Disallow copy semantics to ensure the runtime object can only be freed
481     // once. Copy semantics could be enabled if some sort of reference counting
482     // or deep-copy system for runtime objects is added later.
483     Compilation(const Compilation&) = delete;
484     Compilation& operator=(const Compilation&) = delete;
485 
486     // Move semantics to remove access to the runtime object from the wrapper
487     // object that is being moved. This ensures the runtime object will be
488     // freed only once.
489     Compilation(Compilation&& other) { *this = std::move(other); }
490     Compilation& operator=(Compilation&& other) {
491         if (this != &other) {
492             NNAPI_CALL(ANeuralNetworksCompilation_free(mCompilation));
493             mCompilation = other.mCompilation;
494             other.mCompilation = nullptr;
495         }
496         return *this;
497     }
498 
499     Result setPreference(ExecutePreference preference) {
500         return static_cast<Result>(NNAPI_CALL(ANeuralNetworksCompilation_setPreference(
501                 mCompilation, static_cast<int32_t>(preference))));
502     }
503 
504     Result setPriority(ExecutePriority priority) {
505         return static_cast<Result>(NNAPI_CALL(ANeuralNetworksCompilation_setPriority(
506                 mCompilation, static_cast<int32_t>(priority))));
507     }
508 
509     Result setCaching(const std::string& cacheDir, const std::vector<uint8_t>& token) {
510         if (token.size() != ANEURALNETWORKS_BYTE_SIZE_OF_CACHE_TOKEN) {
511             return Result::BAD_DATA;
512         }
513         return static_cast<Result>(NNAPI_CALL(ANeuralNetworksCompilation_setCaching(
514                 mCompilation, cacheDir.c_str(), token.data())));
515     }
516 
517     Result finish() {
518         return static_cast<Result>(NNAPI_CALL(ANeuralNetworksCompilation_finish(mCompilation)));
519     }
520 
521     Result getPreferredMemoryAlignmentForInput(uint32_t index, uint32_t* alignment) const {
522         if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
523             return static_cast<Result>(
524                     NNAPI_CALL(ANeuralNetworksCompilation_getPreferredMemoryAlignmentForInput(
525                             mCompilation, index, alignment)));
526         } else {
527             return Result::FEATURE_LEVEL_TOO_LOW;
528         }
529     };
530 
531     Result getPreferredMemoryPaddingForInput(uint32_t index, uint32_t* padding) const {
532         if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
533             return static_cast<Result>(
534                     NNAPI_CALL(ANeuralNetworksCompilation_getPreferredMemoryPaddingForInput(
535                             mCompilation, index, padding)));
536         } else {
537             return Result::FEATURE_LEVEL_TOO_LOW;
538         }
539     };
540 
541     Result getPreferredMemoryAlignmentForOutput(uint32_t index, uint32_t* alignment) const {
542         if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
543             return static_cast<Result>(
544                     NNAPI_CALL(ANeuralNetworksCompilation_getPreferredMemoryAlignmentForOutput(
545                             mCompilation, index, alignment)));
546         } else {
547             return Result::FEATURE_LEVEL_TOO_LOW;
548         }
549     };
550 
551     Result getPreferredMemoryPaddingForOutput(uint32_t index, uint32_t* padding) const {
552         if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
553             return static_cast<Result>(
554                     NNAPI_CALL(ANeuralNetworksCompilation_getPreferredMemoryPaddingForOutput(
555                             mCompilation, index, padding)));
556         } else {
557             return Result::FEATURE_LEVEL_TOO_LOW;
558         }
559     };
560 
561     ANeuralNetworksCompilation* getHandle() const { return mCompilation; }
562 
563 #ifdef NNTEST_SLTS
564    protected:
565     // Takes the ownership of ANeuralNetworksCompilation.
566     Compilation(const NnApiSupportLibrary* nnapi, ANeuralNetworksCompilation* compilation)
567         : mNnApi(nnapi), mCompilation(compilation) {}
568 
569    private:
570     const NnApiSupportLibrary* mNnApi = nullptr;
571 #else
572    private:
573 #endif
574     ANeuralNetworksCompilation* mCompilation = nullptr;
575 };
576 
577 class Execution {
578    public:
579 #ifdef NNTEST_SLTS
580     Execution(const NnApiSupportLibrary* nnapi, const Compilation* compilation) : mNnApi(nnapi) {
581 #else
582     Execution(const Compilation* compilation) {
583 #endif
584         int result =
585                 NNAPI_CALL(ANeuralNetworksExecution_create(compilation->getHandle(), &mExecution));
586         if (result != 0) {
587             // TODO Handle the error
588         }
589     }
590 
591     ~Execution() {
592         if (mExecution) {
593             NNAPI_CALL(ANeuralNetworksExecution_free(mExecution));
594         }
595     }
596 
597     // Disallow copy semantics to ensure the runtime object can only be freed
598     // once. Copy semantics could be enabled if some sort of reference counting
599     // or deep-copy system for runtime objects is added later.
600     Execution(const Execution&) = delete;
601     Execution& operator=(const Execution&) = delete;
602 
603     // Move semantics to remove access to the runtime object from the wrapper
604     // object that is being moved. This ensures the runtime object will be
605     // freed only once.
606     Execution(Execution&& other) { *this = std::move(other); }
607     Execution& operator=(Execution&& other) {
608         if (this != &other) {
609             if (mExecution) {
610                 NNAPI_CALL(ANeuralNetworksExecution_free(mExecution));
611             }
612             mExecution = other.mExecution;
613             other.mExecution = nullptr;
614         }
615         return *this;
616     }
617 
618     Result setInput(uint32_t index, const void* buffer, size_t length,
619                     const ANeuralNetworksOperandType* type = nullptr) {
620         return static_cast<Result>(NNAPI_CALL(
621                 ANeuralNetworksExecution_setInput(mExecution, index, type, buffer, length)));
622     }
623 
624     Result setInputFromMemory(uint32_t index, const Memory* memory, uint32_t offset,
625                               uint32_t length, const ANeuralNetworksOperandType* type = nullptr) {
626         return static_cast<Result>(NNAPI_CALL(ANeuralNetworksExecution_setInputFromMemory(
627                 mExecution, index, type, memory->get(), offset, length)));
628     }
629 
630     Result setOutput(uint32_t index, void* buffer, size_t length,
631                      const ANeuralNetworksOperandType* type = nullptr) {
632         return static_cast<Result>(NNAPI_CALL(
633                 ANeuralNetworksExecution_setOutput(mExecution, index, type, buffer, length)));
634     }
635 
636     Result setOutputFromMemory(uint32_t index, const Memory* memory, uint32_t offset,
637                                uint32_t length, const ANeuralNetworksOperandType* type = nullptr) {
638         return static_cast<Result>(NNAPI_CALL(ANeuralNetworksExecution_setOutputFromMemory(
639                 mExecution, index, type, memory->get(), offset, length)));
640     }
641 
642     Result enableInputAndOutputPadding(bool enable) {
643         if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
644             return static_cast<Result>(NNAPI_CALL(
645                     ANeuralNetworksExecution_enableInputAndOutputPadding(mExecution, enable)));
646         } else {
647             return Result::FEATURE_LEVEL_TOO_LOW;
648         }
649     }
650 
651     Result setReusable(bool reusable) {
652         if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
653             return static_cast<Result>(
654                     NNAPI_CALL(ANeuralNetworksExecution_setReusable(mExecution, reusable)));
655         } else {
656             return Result::FEATURE_LEVEL_TOO_LOW;
657         }
658     }
659 
660 #ifndef NNTEST_SLTS
661     Result startCompute(Event* event) {
662         ANeuralNetworksEvent* ev = nullptr;
663         Result result = static_cast<Result>(
664                 NNAPI_CALL(ANeuralNetworksExecution_startCompute(mExecution, &ev)));
665         event->set(ev);
666         return result;
667     }
668 
669     Result startComputeWithDependencies(const std::vector<const Event*>& dependencies,
670                                         uint64_t duration, Event* event) {
671         std::vector<const ANeuralNetworksEvent*> deps(dependencies.size());
672         std::transform(dependencies.begin(), dependencies.end(), deps.begin(),
673                        [](const Event* e) { return e->getHandle(); });
674         ANeuralNetworksEvent* ev = nullptr;
675         Result result = static_cast<Result>(
676                 NNAPI_CALL(ANeuralNetworksExecution_startComputeWithDependencies(
677                         mExecution, deps.data(), deps.size(), duration, &ev)));
678         event->set(ev);
679         return result;
680     }
681 #endif
682 
683     Result compute() {
684         return static_cast<Result>(NNAPI_CALL(ANeuralNetworksExecution_compute(mExecution)));
685     }
686 
687     Result getOutputOperandDimensions(uint32_t index, std::vector<uint32_t>* dimensions) {
688         uint32_t rank = 0;
689         Result result = static_cast<Result>(NNAPI_CALL(
690                 ANeuralNetworksExecution_getOutputOperandRank(mExecution, index, &rank)));
691         dimensions->resize(rank);
692         if ((result != Result::NO_ERROR && result != Result::OUTPUT_INSUFFICIENT_SIZE) ||
693             rank == 0) {
694             return result;
695         }
696         result = static_cast<Result>(NNAPI_CALL(ANeuralNetworksExecution_getOutputOperandDimensions(
697                 mExecution, index, dimensions->data())));
698         return result;
699     }
700 
701    private:
702 #ifdef NNTEST_SLTS
703     const NnApiSupportLibrary* mNnApi = nullptr;
704 #endif
705     ANeuralNetworksExecution* mExecution = nullptr;
706 };
707 
708 }  // namespace wrapper
709 }  // namespace nn
710 }  // namespace android
711 
712 #endif  //  ANDROID_FRAMEWORKS_ML_NN_RUNTIME_NEURAL_NETWORKS_WRAPPER_H
713