IVGCVSW-6313 Support pre-importing outputs

 * Add ClearImportedInputs/Outputs function to IRuntime
 * Add UnImport function to ITensorHandle
 * Remove mutex from IWorkingMemHandle

Change-Id: I34c9b6e1618755e10f3b4597afa1d9a9ea97e5fe
Signed-off-by: Finn Williams <finwil01@e127804.cambridge.arm.com>
diff --git a/src/armnn/test/RuntimeTests.cpp b/src/armnn/test/RuntimeTests.cpp
index abf13f5..397a545 100644
--- a/src/armnn/test/RuntimeTests.cpp
+++ b/src/armnn/test/RuntimeTests.cpp
@@ -64,11 +64,10 @@
 TEST_CASE("RuntimePreImportInputs")
 {
     armnn::IRuntime::CreationOptions options;
-    armnn::IRuntimePtr               runtime(armnn::IRuntime::Create(options));
-
-    armnn::NetworkId   networkIdentifier1 = 1;
-
+    armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
+    armnn::NetworkId networkId = 1;
     armnn::INetworkPtr testNetwork(armnn::INetwork::Create());
+
     auto inputLayer1 = testNetwork->AddInputLayer(0, "input 1 layer");
     auto inputLayer2 = testNetwork->AddInputLayer(1, "input 2 layer");
     auto addLayer = testNetwork->AddAdditionLayer("add layer");
@@ -78,17 +77,18 @@
 
     inputLayer1->GetOutputSlot(0).Connect(addLayer->GetInputSlot(0));
     inputLayer1->GetOutputSlot(0).SetTensorInfo(tensorInfo);
+
     inputLayer2->GetOutputSlot(0).Connect(addLayer->GetInputSlot(1));
     inputLayer2->GetOutputSlot(0).SetTensorInfo(tensorInfo);
 
     addLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
     addLayer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
 
-    std::vector<armnn::BackendId> backends = { armnn::Compute::CpuRef };
+    std::vector<armnn::BackendId> backends = {armnn::Compute::CpuRef};
 
     std::string er;
     armnn::INetworkProperties networkProperties(true, MemorySource::Malloc, MemorySource::Undefined);
-    runtime->LoadNetwork(networkIdentifier1,
+    runtime->LoadNetwork(networkId,
                          Optimize(*testNetwork, backends, runtime->GetDeviceSpec()),
                          er,
                          networkProperties);
@@ -99,73 +99,233 @@
 
     ConstTensor inputTensor1({{4}, armnn::DataType::Signed32}, inputData1.data());
     ConstTensor inputTensor2({{4}, armnn::DataType::Signed32}, inputData2.data());
-
     Tensor outputTensor({{4}, armnn::DataType::Signed32}, output.data());
 
-    auto importedInputVec1 = runtime->ImportInputs(networkIdentifier1, {{0, inputTensor1}});
+    auto importedInputVec1 = runtime->ImportInputs(networkId, {{0, inputTensor1}});
     CHECK(importedInputVec1.size() == 1);
     CHECK(importedInputVec1[0] == 0);
 
-    auto memHandle = runtime->CreateWorkingMemHandle(networkIdentifier1);
+    auto memHandle = runtime->CreateWorkingMemHandle(networkId);
 
     runtime->Execute(*memHandle.get(), {{1, inputTensor2}}, {{2, outputTensor}}, {0 /* pre-imported id */});
-    for (auto val : output)
-    {
+    for (auto val: output) {
         CHECK(val == 30);
     }
 
-    auto importedInputVec2 = runtime->ImportInputs(networkIdentifier1, {{1, inputTensor2}});
+    auto importedInputVec2 = runtime->ImportInputs(networkId, {{1, inputTensor2}});
     CHECK(importedInputVec2.size() == 1);
     CHECK(importedInputVec2[0] == 1);
 
     runtime->Execute(*memHandle.get(), {{0, inputTensor1}}, {{2, outputTensor}}, {1 /* pre-imported id */});
-    for (auto val : output)
-    {
+    for (auto val: output) {
         CHECK(val == 30);
     }
 
     runtime->Execute(*memHandle.get(), {}, {{2, outputTensor}}, {0, 1});
-    for (auto val : output)
-    {
+    for (auto val: output) {
         CHECK(val == 30);
     }
-
     // Duplicate ImportedInputId and LayerBindingId
-    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(),{},{{2, outputTensor}},{0, 0});
-                    , armnn::InvalidArgumentException);
-
+    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), {}, {{2, outputTensor}}, {0, 0});,
+                    armnn::InvalidArgumentException);
     // Duplicate LayerBindingId
-    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), {{1, inputTensor2}}, {{2, outputTensor}},{1});
-                    , armnn::InvalidArgumentException);
-
+    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), {{1, inputTensor2}}, {{2, outputTensor}}, {1});,
+                    armnn::InvalidArgumentException);
     // Incorrect ImportedInputId
-    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), {{1, inputTensor2}}, {{2, outputTensor}},{10});
-                    , armnn::InvalidArgumentException);
-
+    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), {{1, inputTensor2}}, {{2, outputTensor}}, {10});,
+                    armnn::InvalidArgumentException);
     // Incorrect LayerBindingId
-    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), {{-2, inputTensor2}}, {{2, outputTensor}},{1});
-                    , armnn::InvalidArgumentException);
-
+    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), {{-2, inputTensor2}}, {{2, outputTensor}}, {1});,
+                    armnn::InvalidArgumentException);
     // Incorrect layer binding id and ImportedInputId
-    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), {{-2, inputTensor2}}, {{2, outputTensor}},{10});
-                    , armnn::InvalidArgumentException);
-
-
-    auto importedInputVec3 = runtime->ImportInputs(networkIdentifier1, {{1, inputTensor2}});
+    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), {{-2, inputTensor2}}, {{2, outputTensor}}, {10});,
+                    armnn::InvalidArgumentException);
+    auto importedInputVec3 = runtime->ImportInputs(networkId, {{1, inputTensor2}});
     CHECK(importedInputVec3[0] == 2);
     // Too many ImportedInputIds
-    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), {}, {{2, outputTensor}},{0, 1, 2});
-                    , armnn::InvalidArgumentException);
-
+    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), {}, {{2, outputTensor}}, {0, 1, 2});,
+                    armnn::InvalidArgumentException);
     // Too many InputTensors
     CHECK_THROWS_AS(runtime->Execute(*memHandle.get(),
-                                     {{0, inputTensor2}, {1, inputTensor2}, {2, inputTensor2}},
-                                     {{2, outputTensor}});
-                    , armnn::InvalidArgumentException);
-
+                                     {{0, inputTensor2},
+                                      {1, inputTensor2},
+                                      {2, inputTensor2}},
+                                      {{2, outputTensor}});, armnn::InvalidArgumentException);
     // Too few ImportedInputIds
-    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), {}, {{2, outputTensor}},{0});
-                    , armnn::InvalidArgumentException);
+    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), {}, {{2, outputTensor}}, {0});,
+                    armnn::InvalidArgumentException);
+    runtime->ClearImportedInputs(networkId, {1});
+    runtime->Execute(*memHandle.get(), {{1, inputTensor2}}, {{2, outputTensor}}, {0}, {});
+    for (auto val: output) {
+        CHECK(val == 30);
+    }
+    // Using deleted pre-imported input
+    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), {}, {{2, outputTensor}}, {0, 1}, {});,
+                    armnn::InvalidArgumentException);
+
+    // Trying to delete deleted pre-imported tensor
+    CHECK_THROWS_AS(runtime->ClearImportedInputs(networkId, {1});, armnn::InvalidArgumentException);
+
+    // Trying to delete unknown pre-imported tensor
+    CHECK_THROWS_AS(runtime->ClearImportedInputs(networkId, {10});, armnn::InvalidArgumentException);
+}
+TEST_CASE("RuntimePreImportOutputs")
+{
+    armnn::IRuntime::CreationOptions options;
+    armnn::IRuntimePtr               runtime(armnn::IRuntime::Create(options));
+
+    armnn::NetworkId   networkId = 1;
+
+    armnn::INetworkPtr testNetwork(armnn::INetwork::Create());
+    TensorInfo tensorInfo{{4}, armnn::DataType::Float32};
+
+    auto inputLayer1 = testNetwork->AddInputLayer(0, "input 1 layer");
+    inputLayer1->GetOutputSlot(0).SetTensorInfo(tensorInfo);
+
+    ActivationDescriptor activationDescriptor;
+    activationDescriptor.m_Function = ActivationFunction::BoundedReLu;
+    activationDescriptor.m_A = 2.0f;
+    activationDescriptor.m_B = 0.0f;
+    auto activationLayer1 = testNetwork->AddActivationLayer(activationDescriptor, "add layer");
+    auto outputLayer1 = testNetwork->AddOutputLayer(2, "output layer");
+
+    inputLayer1->GetOutputSlot(0).Connect(activationLayer1->GetInputSlot(0));
+
+    activationLayer1->GetOutputSlot(0).Connect(outputLayer1->GetInputSlot(0));
+    activationLayer1->GetOutputSlot(0).SetTensorInfo(tensorInfo);
+
+    auto inputLayer2 = testNetwork->AddInputLayer(1, "input 1 layer");
+
+    activationDescriptor.m_A = 4.0f;
+    activationDescriptor.m_B = 2.0f;
+    auto activationLayer2 = testNetwork->AddActivationLayer(activationDescriptor, "add layer");
+    auto outputLayer2 = testNetwork->AddOutputLayer(3, "output layer");
+
+    inputLayer2->GetOutputSlot(0).Connect(activationLayer2->GetInputSlot(0));
+    inputLayer2->GetOutputSlot(0).SetTensorInfo(tensorInfo);
+
+    activationLayer2->GetOutputSlot(0).Connect(outputLayer2->GetInputSlot(0));
+    activationLayer2->GetOutputSlot(0).SetTensorInfo(tensorInfo);
+
+    std::vector<armnn::BackendId> backends = { armnn::Compute::CpuRef };
+
+    std::string er;
+    armnn::INetworkProperties networkProperties(true, MemorySource::Malloc, MemorySource::Malloc);
+    runtime->LoadNetwork(networkId,
+                         Optimize(*testNetwork, backends, runtime->GetDeviceSpec()),
+                         er,
+                         networkProperties);
+
+    std::vector<float> inputData1(4, 1.0f);
+    std::vector<float> inputData2(4, 3.0f);
+
+    std::vector<float> outputData1(4);
+    std::vector<float> outputData2(4);
+
+    ConstTensor inputTensor1(tensorInfo, inputData1.data());
+    ConstTensor inputTensor2(tensorInfo, inputData2.data());
+
+    Tensor outputTensor1{tensorInfo, outputData1.data()};
+    Tensor outputTensor2{tensorInfo, outputData2.data()};
+
+    InputTensors inputTensors = {{0, inputTensor1}, {1, inputTensor2}};
+
+    std::pair<LayerBindingId, class Tensor> output1{2, outputTensor1};
+    std::pair<LayerBindingId, class Tensor> output2{3, outputTensor2};
+
+    auto testOutputs = [&]()
+    {
+        for (auto val : outputData1)
+        {
+                    CHECK(val == 1.0f);
+        }
+
+        for (auto val : outputData2)
+        {
+                    CHECK(val == 3.0f);
+        }
+    };
+
+    auto memHandle = runtime->CreateWorkingMemHandle(networkId);
+
+    runtime->Execute(*memHandle.get(),inputTensors, {output1, output2});
+    testOutputs();
+
+    auto importedOutputVec = runtime->ImportOutputs(networkId, {output1, output2 });
+    CHECK(importedOutputVec.size() == 2);
+    CHECK(importedOutputVec[0] == 0);
+    CHECK(importedOutputVec[1] == 1);
+
+    runtime->Execute(*memHandle.get(), inputTensors, {}, {}, importedOutputVec);
+    testOutputs();
+
+    runtime->Execute(*memHandle.get(), inputTensors, {output1}, {}, {1});
+    testOutputs();
+
+    runtime->Execute(*memHandle.get(), inputTensors, {output2}, {}, {0});
+    testOutputs();
+
+    auto importedInputVec = runtime->ImportInputs(networkId, inputTensors);
+    CHECK(importedInputVec.size() == 2);
+    CHECK(importedInputVec[0] == 0);
+    CHECK(importedInputVec[1] == 1);
+
+    runtime->Execute(*memHandle.get(), {}, {}, importedInputVec, importedOutputVec);
+    testOutputs();
+
+    runtime->Execute(*memHandle.get(), {{0, inputTensor1}}, {output2}, {1}, {0});
+    testOutputs();
+
+    // Too many ids
+    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), inputTensors, {output1, output2}, {}, {0, 1});,
+                    armnn::InvalidArgumentException);
+
+    // Duplicate ids
+    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), inputTensors, {output2}, {}, {1});,
+                    armnn::InvalidArgumentException);
+
+    // Duplicate ids
+    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), inputTensors, {output1, output1}, {}, {});,
+                    armnn::InvalidArgumentException);
+
+    // Duplicate ids
+    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), inputTensors, {}, {}, {0, 0}),
+                    armnn::InvalidArgumentException);
+
+    // Unknown id
+    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), inputTensors, {output1}, {}, {3});,
+                    armnn::InvalidArgumentException);
+
+    // Unknown id
+    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), inputTensors, {{4, outputTensor2}}, {}, {1});,
+                    armnn::InvalidArgumentException);
+
+    // Input id for output
+    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), inputTensors, {{0, outputTensor2}}, {}, {1});,
+                    armnn::InvalidArgumentException);
+
+    // Input id for output
+    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), inputTensors, {{0, outputTensor2}}, {}, {1});,
+                    armnn::InvalidArgumentException);
+
+    // Output id for input
+    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), {{2, inputTensor1}}, {{0, outputTensor2}}, {1}, {1, 0});,
+                    armnn::InvalidArgumentException);
+
+    runtime->ClearImportedOutputs(networkId, {1});
+
+    runtime->Execute(*memHandle.get(), inputTensors, {output2}, {}, {0});
+    testOutputs();
+
+    // Trying to use deleted pre-imported tensor
+    CHECK_THROWS_AS(runtime->Execute(*memHandle.get(), inputTensors, {}, {}, importedOutputVec),
+                    armnn::InvalidArgumentException);
+
+    // Trying to delete deleted pre-imported tensor
+    CHECK_THROWS_AS(runtime->ClearImportedOutputs(networkId, {1});, armnn::InvalidArgumentException);
+
+    // Trying to delete unknown pre-imported tensor
+    CHECK_THROWS_AS(runtime->ClearImportedOutputs(networkId, {10});, armnn::InvalidArgumentException);
 }
 
 // Note: the current builds we don't do valgrind and gperftools based leak checking at the same