IVGCVSW-4157 Pass custom options directly to backends

Change-Id: I98cfb913dbd00cb94bdb5dbe82753ca147f7f671
Signed-off-by: Derek Lamberti <derek.lamberti@arm.com>
diff --git a/src/armnn/test/RuntimeTests.cpp b/src/armnn/test/RuntimeTests.cpp
index 4fd847a..642f334 100644
--- a/src/armnn/test/RuntimeTests.cpp
+++ b/src/armnn/test/RuntimeTests.cpp
@@ -288,6 +288,55 @@
     BOOST_TEST(!optNet);
 }
 
+BOOST_AUTO_TEST_CASE(RuntimeBackendOptions)
+{
+    using namespace armnn;
+
+    IRuntime::CreationOptions creationOptions;
+    auto& backendOptions = creationOptions.m_BackendOptions;
+
+
+    // Define Options on explicit construction
+    BackendOptions options1("FakeBackend1",
+                           {
+                               {"Option1", 1.3f},
+                               {"Option2", true}
+                           });
+
+    // Add an option after construction
+    options1.AddOption({"Option3", "some_value"});
+
+    // Add the options to CreationOptions struct
+    backendOptions.push_back(options1);
+
+    // Add more Options via inplace explicit construction
+    backendOptions.emplace_back(
+        BackendOptions{"FakeBackend1",
+                       {{"Option4", 42}}
+                        });
+
+
+    // First group
+    BOOST_TEST(backendOptions[0].GetBackendId().Get() == "FakeBackend1");
+    BOOST_TEST(backendOptions[0].GetOption(0).GetName() == "Option1");
+    BOOST_TEST(backendOptions[0].GetOption(0).GetValue().IsFloat() == true);
+    BOOST_TEST(backendOptions[0].GetOption(0).GetValue().AsFloat() == 1.3f);
+
+    BOOST_TEST(backendOptions[0].GetOption(1).GetName() == "Option2");
+    BOOST_TEST(backendOptions[0].GetOption(1).GetValue().IsBool() == true);
+    BOOST_TEST(backendOptions[0].GetOption(1).GetValue().AsBool() == true);
+
+    BOOST_TEST(backendOptions[0].GetOption(2).GetName() == "Option3");
+    BOOST_TEST(backendOptions[0].GetOption(2).GetValue().IsString() == true);
+    BOOST_TEST(backendOptions[0].GetOption(2).GetValue().AsString() == "some_value");
+
+    // Second group
+    BOOST_TEST(backendOptions[1].GetBackendId().Get() == "FakeBackend1");
+    BOOST_TEST(backendOptions[1].GetOption(0).GetName() == "Option4");
+    BOOST_TEST(backendOptions[1].GetOption(0).GetValue().IsInt() == true);
+    BOOST_TEST(backendOptions[1].GetOption(0).GetValue().AsInt() == 42);
+}
+
 BOOST_AUTO_TEST_CASE(ProfilingDisable)
 {
     using namespace armnn;
@@ -635,7 +684,7 @@
                                            LabelsAndEventClasses::TYPE_GUID,
                                            readableData,
                                            offset);
-    
+
     bufferManager.MarkRead(readableBuffer);
 
     // Creates structures for input & output.
diff --git a/src/backends/cl/ClBackendContext.cpp b/src/backends/cl/ClBackendContext.cpp
index a82391c..b435c29 100644
--- a/src/backends/cl/ClBackendContext.cpp
+++ b/src/backends/cl/ClBackendContext.cpp
@@ -4,23 +4,25 @@
 //
 
 #include "ClBackendContext.hpp"
+#include "ClContextControl.hpp"
 
 #include <armnn/Logging.hpp>
 
-#include "ClContextControl.hpp"
-
 #include <arm_compute/core/CL/OpenCL.h>
 #include <arm_compute/core/CL/CLKernelLibrary.h>
 #include <arm_compute/runtime/CL/CLScheduler.h>
+#include <arm_compute/runtime/CL/CLTunerTypes.h>
+
+#include <boost/polymorphic_cast.hpp>
 
 namespace armnn
 {
 
 struct ClBackendContext::ClContextControlWrapper
 {
-    ClContextControlWrapper(IGpuAccTunedParameters* clTunedParameters,
+    ClContextControlWrapper(arm_compute::CLTuner* tuner,
                             bool profilingEnabled)
-        : m_ClContextControl(clTunedParameters, profilingEnabled)
+        : m_ClContextControl(tuner, profilingEnabled)
     {}
 
     bool Sync()
@@ -56,13 +58,172 @@
     ClContextControl m_ClContextControl;
 };
 
+std::string LowerString(std::string value)
+{
+    std::transform(value.begin(), value.end(), value.begin(),
+                   [](unsigned char c){ return std::tolower(c); });
+
+    return value;
+}
+
+enum class TuningLevel
+{
+    None,
+    Rapid,
+    Normal,
+    Exhaustive
+};
+
+
+TuningLevel ParseTuningLevel(const BackendOptions::Var& value, TuningLevel defaultValue)
+{
+    if (value.IsInt())
+    {
+        int v = value.IsInt();
+        if (v > static_cast<int>(TuningLevel::Exhaustive) ||
+            v < static_cast<int>(TuningLevel::None))
+        {
+            ARMNN_LOG(warning) << "Invalid GpuAcc tuning level ("<< v << ") selected. "
+                                  "Using default(" << static_cast<int>(defaultValue) << ")";
+        } else
+        {
+            return static_cast<TuningLevel>(v);
+        }
+    }
+    return defaultValue;
+}
+
+bool ParseBoolean(const BackendOptions::Var& value, bool defaultValue)
+{
+    if (value.IsBool())
+    {
+        return value.AsBool();
+    }
+
+    return defaultValue;
+}
+
+std::string ParseFile(const BackendOptions::Var& value, std::string defaultValue)
+{
+    if (value.IsString())
+    {
+        return value.AsString();
+    }
+    return defaultValue;
+}
+
+template <typename F>
+void ParseOptions(const std::vector<BackendOptions>& options, BackendId backend, F f)
+{
+    for (auto optionsGroup : options)
+    {
+        if (optionsGroup.GetBackendId() == backend)
+        {
+            for (size_t i=0; i < optionsGroup.GetOptionCount(); i++)
+            {
+                const BackendOptions::BackendOption option = optionsGroup.GetOption(i);
+                f(option.GetName(), option.GetValue());
+            }
+        }
+    }
+}
 
 ClBackendContext::ClBackendContext(const IRuntime::CreationOptions& options)
     : IBackendContext(options)
-    , m_ClContextControlWrapper(
-        std::make_unique<ClContextControlWrapper>(options.m_GpuAccTunedParameters.get(),
-                                                  options.m_EnableGpuProfiling))
 {
+    bool kernelProfiling = options.m_EnableGpuProfiling;
+    const TuningLevel defaultTuningLevel = TuningLevel::None;
+    auto tuningLevel = defaultTuningLevel;
+    m_TuningFile = "";
+
+
+    arm_compute::CLTuner* tuner = nullptr;
+    if (m_TuningFile.empty() == false)
+    {
+        bool useLegacyTunerAPI = options.m_GpuAccTunedParameters.get() != nullptr;
+        if (useLegacyTunerAPI)
+        {
+            auto clTunerParams = boost::polymorphic_downcast<ClTunedParameters*>(
+                                    options.m_GpuAccTunedParameters.get());
+            auto clTuner = &clTunerParams->m_Tuner;
+
+            if (clTuner)
+            {
+                auto ConvertTuningLevel = [](IGpuAccTunedParameters::TuningLevel level)
+                    {
+                        switch(level)
+                        {
+                            case IGpuAccTunedParameters::TuningLevel::Rapid:
+                                return arm_compute::CLTunerMode::RAPID;
+                            case IGpuAccTunedParameters::TuningLevel::Normal:
+                                return arm_compute::CLTunerMode::NORMAL;
+                            case IGpuAccTunedParameters::TuningLevel::Exhaustive:
+                                return arm_compute::CLTunerMode::EXHAUSTIVE;
+                            default:
+                            {
+                                BOOST_ASSERT_MSG(false, "Tuning level not recognised.");
+                                return arm_compute::CLTunerMode::NORMAL;
+                            }
+                        }
+                    };
+
+                clTuner->set_tuner_mode(ConvertTuningLevel(clTunerParams->m_TuningLevel));
+                clTuner->set_tune_new_kernels(
+                    clTunerParams->m_Mode == armnn::IGpuAccTunedParameters::Mode::UpdateTunedParameters);
+            }
+        }
+        else //New backend options API
+        {
+            ParseOptions(options.m_BackendOptions, "GpuAcc", [&](std::string name, const BackendOptions::Var& value)
+                {
+                    if (name == "KernelProfilingEnabled")
+                    {
+                        kernelProfiling |= ParseBoolean(value, false);
+                    } else if (name == "TuningFile")
+                    {
+                        m_TuningFile = ParseFile(value, "");
+                    } else if (name == "TuningLevel")
+                    {
+                        tuningLevel = ParseTuningLevel(value, defaultTuningLevel);
+                    }
+                });
+
+            // Create the tuner, in tuning mode initially.
+            m_Tuner = std::make_unique<arm_compute::CLTuner>(true);
+
+            switch (tuningLevel)
+            {
+                case TuningLevel::Rapid:
+                    m_Tuner->set_tuner_mode(arm_compute::CLTunerMode::RAPID);
+                    break;
+                case TuningLevel::Normal:
+                    m_Tuner->set_tuner_mode(arm_compute::CLTunerMode::NORMAL);
+                    break;
+                case TuningLevel::Exhaustive:
+                    m_Tuner->set_tuner_mode(arm_compute::CLTunerMode::EXHAUSTIVE);
+                    break;
+                case TuningLevel::None:
+                default:
+                    m_Tuner->set_tune_new_kernels(false); // Turn of tuning. Set to "use" only mode.
+                    break;
+            }
+
+            try
+            {
+                m_Tuner->load_from_file(m_TuningFile.c_str());
+            } catch (const std::exception& e)
+            {
+                ARMNN_LOG(warning) << "Could not load GpuAcc tuner data file.";
+            }
+
+            tuner = m_Tuner.get();
+        }
+    }
+
+    m_ClContextControlWrapper = std::make_unique<ClContextControlWrapper>(
+            tuner,
+            kernelProfiling
+        );
 }
 
 bool ClBackendContext::BeforeLoadNetwork(NetworkId)
@@ -103,6 +264,17 @@
 
 ClBackendContext::~ClBackendContext()
 {
+    if (m_Tuner && !m_TuningFile.empty())
+    {
+        try
+        {
+            m_Tuner->save_to_file(m_TuningFile.c_str());
+        }
+        catch(const std::exception& e)
+        {
+            ARMNN_LOG(warning) << "Could not save GpuAcc tuner data to file " << m_TuningFile;
+        }
+    }
 }
 
 } // namespace armnn
\ No newline at end of file
diff --git a/src/backends/cl/ClBackendContext.hpp b/src/backends/cl/ClBackendContext.hpp
index 8d49602..bcac0d2 100644
--- a/src/backends/cl/ClBackendContext.hpp
+++ b/src/backends/cl/ClBackendContext.hpp
@@ -8,6 +8,8 @@
 #include <unordered_set>
 #include <mutex>
 
+#include <arm_compute/runtime/CL/CLTuner.h>
+
 namespace armnn
 {
 
@@ -31,6 +33,8 @@
 
     std::unordered_set<NetworkId> m_NetworkIds;
 
+    std::unique_ptr<arm_compute::CLTuner> m_Tuner;
+    std::string m_TuningFile;
 };
 
 } // namespace armnn
\ No newline at end of file
diff --git a/src/backends/cl/ClContextControl.cpp b/src/backends/cl/ClContextControl.cpp
index cf5ae64..72c8e9f 100644
--- a/src/backends/cl/ClContextControl.cpp
+++ b/src/backends/cl/ClContextControl.cpp
@@ -27,9 +27,9 @@
 namespace armnn
 {
 
-ClContextControl::ClContextControl(IGpuAccTunedParameters* clTunedParameters,
+ClContextControl::ClContextControl(arm_compute::CLTuner *tuner,
                                    bool profilingEnabled)
-    : m_clTunedParameters(boost::polymorphic_downcast<ClTunedParameters*>(clTunedParameters))
+    : m_Tuner(tuner)
     , m_ProfilingEnabled(profilingEnabled)
 {
     // Ignore m_ProfilingEnabled if unused to avoid compiling problems when ArmCompute is disabled.
@@ -97,7 +97,7 @@
     DoLoadOpenClRuntime(false);
 }
 
-void ClContextControl::DoLoadOpenClRuntime(bool useTunedParameters)
+void ClContextControl::DoLoadOpenClRuntime(bool updateTunedParameters)
 {
     cl::Device device = cl::Device::getDefault();
     cl::Context context;
@@ -133,8 +133,8 @@
 
         // NOTE: In this specific case profiling has to be enabled on the command queue
         // in order for the CLTuner to work.
-        bool profilingNeededForClTuner = useTunedParameters && m_clTunedParameters &&
-            m_clTunedParameters->m_Mode == IGpuAccTunedParameters::Mode::UpdateTunedParameters;
+        bool profilingNeededForClTuner = updateTunedParameters && m_Tuner &&
+            m_Tuner->tune_new_kernels();
 
         if (m_ProfilingEnabled || profilingNeededForClTuner)
         {
@@ -156,34 +156,7 @@
 
     // Note the first argument (path to cl source code) will be ignored as they should be embedded in the armcompute.
     arm_compute::CLKernelLibrary::get().init(".", context, device);
-
-    arm_compute::ICLTuner* tuner = nullptr;
-    if (useTunedParameters && m_clTunedParameters)
-    {
-        tuner = &m_clTunedParameters->m_Tuner;
-        auto clTuner = boost::polymorphic_downcast<arm_compute::CLTuner*>(tuner);
-
-        auto ConvertTuningLevel = [](IGpuAccTunedParameters::TuningLevel level)
-        {
-            switch(level)
-            {
-                case IGpuAccTunedParameters::TuningLevel::Rapid:
-                    return arm_compute::CLTunerMode::RAPID;
-                case IGpuAccTunedParameters::TuningLevel::Normal:
-                    return arm_compute::CLTunerMode::NORMAL;
-                case IGpuAccTunedParameters::TuningLevel::Exhaustive:
-                    return arm_compute::CLTunerMode::EXHAUSTIVE;
-                default:
-                {
-                    BOOST_ASSERT_MSG(false, "Tuning level not recognised.");
-                    return arm_compute::CLTunerMode::NORMAL;
-                }
-            }
-        };
-
-        clTuner->set_tuner_mode(ConvertTuningLevel(m_clTunedParameters->m_TuningLevel));
-    }
-    arm_compute::CLScheduler::get().init(context, commandQueue, device, tuner);
+    arm_compute::CLScheduler::get().init(context, commandQueue, device, m_Tuner);
 }
 
 void ClContextControl::ClearClCache()
@@ -225,7 +198,7 @@
     catch (const std::exception& e)
     {
         throw armnn::Exception(std::string("Failed to load tuned parameters file '") + filename + "': " +
-            e.what());
+                               e.what());
     }
 }
 
@@ -238,7 +211,7 @@
     catch (const std::exception& e)
     {
         throw armnn::Exception(std::string("Failed to save tuned parameters file to '") + filename + "': " +
-            e.what());
+                               e.what());
     }
 }
 
diff --git a/src/backends/cl/ClContextControl.hpp b/src/backends/cl/ClContextControl.hpp
index 8a5abf7..fd27ced 100644
--- a/src/backends/cl/ClContextControl.hpp
+++ b/src/backends/cl/ClContextControl.hpp
@@ -11,15 +11,12 @@
 namespace armnn
 {
 
-class IGpuAccTunedParameters;
-class ClTunedParameters;
-
 // ARM Compute OpenCL context control.
 class ClContextControl
 {
 public:
 
-    ClContextControl(IGpuAccTunedParameters* clTunedParameters = nullptr,
+    ClContextControl(arm_compute::CLTuner* = nullptr,
                      bool profilingEnabled = false);
 
     virtual ~ClContextControl();
@@ -35,9 +32,9 @@
 
 private:
 
-    void DoLoadOpenClRuntime(bool useTunedParameters);
+    void DoLoadOpenClRuntime(bool updateTunedParameters);
 
-    ClTunedParameters* m_clTunedParameters;
+    arm_compute::CLTuner* m_Tuner;
 
     bool m_ProfilingEnabled;
 };