| 747 | } |
| 748 | |
| 749 | TfLiteStatus Subgraph::Invoke() { |
| 750 | if (!consistent_) { |
| 751 | ReportError("Invoke called on model that is not consistent."); |
| 752 | return kTfLiteError; |
| 753 | } |
| 754 | |
| 755 | TfLiteStatus status = kTfLiteOk; |
| 756 | if (state_ == kStateUninvokable) { |
| 757 | ReportError("Invoke called on model that is not ready."); |
| 758 | return kTfLiteError; |
| 759 | } |
| 760 | |
| 761 | // This is only needed for UseNNAPI(true); |
| 762 | if (should_apply_nnapi_delegate_ && !applied_nnapi_delegate_) { |
| 763 | TF_LITE_ENSURE_OK(&context_, ModifyGraphWithDelegate(NnApiDelegate())); |
| 764 | // only need to modify the graph once upon the first invocation. |
| 765 | applied_nnapi_delegate_ = true; |
| 766 | } |
| 767 | |
| 768 | // Invocations are always done in node order. |
| 769 | // Note that calling Invoke repeatedly will cause the original memory plan to |
| 770 | // be reused, unless either ResizeInputTensor() or AllocateTensors() has been |
| 771 | // called. |
| 772 | for (int execution_plan_index = 0; |
| 773 | execution_plan_index < execution_plan_.size(); execution_plan_index++) { |
| 774 | if (execution_plan_index == next_execution_plan_index_to_prepare_) { |
| 775 | TF_LITE_ENSURE_STATUS(PrepareOpsAndTensors()); |
| 776 | TF_LITE_ENSURE(&context_, next_execution_plan_index_to_prepare_ >= |
| 777 | execution_plan_index); |
| 778 | } |
| 779 | int node_index = execution_plan_[execution_plan_index]; |
| 780 | TfLiteNode& node = nodes_and_registration_[node_index].first; |
| 781 | const TfLiteRegistration& registration = |
| 782 | nodes_and_registration_[node_index].second; |
| 783 | TFLITE_SCOPED_OPERATOR_PROFILE(profiler_, node_index); |
| 784 | |
| 785 | // TODO(ycling): This is an extra loop through inputs to check if the data |
| 786 | // need to be copied from Delegate buffer to raw memory, which is often not |
| 787 | // needed. We may want to cache this in prepare to know if this needs to be |
| 788 | // done for a node or not. |
| 789 | for (int i = 0; i < node.inputs->size; ++i) { |
| 790 | int tensor_index = node.inputs->data[i]; |
| 791 | if (tensor_index == kOptionalTensor) { |
| 792 | continue; |
| 793 | } |
| 794 | TfLiteTensor* tensor = &tensors_[tensor_index]; |
| 795 | if (tensor->delegate && tensor->delegate != node.delegate && |
| 796 | tensor->data_is_stale) { |
| 797 | TF_LITE_ENSURE_STATUS(EnsureTensorDataIsReadable(tensor_index)); |
| 798 | } |
| 799 | if (tensor->data.raw == nullptr && tensor->bytes > 0) { |
| 800 | if (registration.builtin_code == kTfLiteBuiltinReshape && i == 1) { |
| 801 | // In general, having a tensor here with no buffer will be an error. |
| 802 | // However, for the reshape operator, the second input tensor is only |
| 803 | // used for the shape, not for the data. Thus, null buffer is ok. |
| 804 | continue; |
| 805 | } else { |
| 806 | // In all other cases, we need to return an error as otherwise we will |