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