Forráskód Böngészése

ONNX gem (#24)

* Add ONNX gem

Signed-off-by: Kuba Ciukiewicz <[email protected]>

* Clean up

Signed-off-by: Kuba Ciukiewicz <[email protected]>

* Change to conform to standards. Fix uninitialized variables.

Signed-off-by: Kuba Ciukiewicz <[email protected]>

Signed-off-by: Kuba Ciukiewicz <[email protected]>
Kuba 2 éve
szülő
commit
9999bd7e10
52 módosított fájl, 1788 hozzáadás és 0 törlés
  1. 0 0
      Gems/ONNX/.gitignore
  2. 18 0
      Gems/ONNX/CMakeLists.txt
  3. 254 0
      Gems/ONNX/Code/CMakeLists.txt
  4. 70 0
      Gems/ONNX/Code/Include/ONNX/Model.h
  5. 47 0
      Gems/ONNX/Code/Include/ONNX/ONNXBus.h
  6. 11 0
      Gems/ONNX/Code/Platform/Android/PAL_android.cmake
  7. 10 0
      Gems/ONNX/Code/Platform/Android/onnx_api_files.cmake
  8. 15 0
      Gems/ONNX/Code/Platform/Android/onnx_private_files.cmake
  9. 15 0
      Gems/ONNX/Code/Platform/Android/onnx_shared_files.cmake
  10. 12 0
      Gems/ONNX/Code/Platform/Linux/PAL_linux.cmake
  11. 10 0
      Gems/ONNX/Code/Platform/Linux/onnx_api_files.cmake
  12. 10 0
      Gems/ONNX/Code/Platform/Linux/onnx_editor_api_files.cmake
  13. 15 0
      Gems/ONNX/Code/Platform/Linux/onnx_private_files.cmake
  14. 15 0
      Gems/ONNX/Code/Platform/Linux/onnx_shared_files.cmake
  15. 11 0
      Gems/ONNX/Code/Platform/Mac/PAL_mac.cmake
  16. 10 0
      Gems/ONNX/Code/Platform/Mac/onnx_api_files.cmake
  17. 10 0
      Gems/ONNX/Code/Platform/Mac/onnx_editor_api_files.cmake
  18. 15 0
      Gems/ONNX/Code/Platform/Mac/onnx_private_files.cmake
  19. 15 0
      Gems/ONNX/Code/Platform/Mac/onnx_shared_files.cmake
  20. 12 0
      Gems/ONNX/Code/Platform/Windows/PAL_windows.cmake
  21. 10 0
      Gems/ONNX/Code/Platform/Windows/onnx_api_files.cmake
  22. 10 0
      Gems/ONNX/Code/Platform/Windows/onnx_editor_api_files.cmake
  23. 15 0
      Gems/ONNX/Code/Platform/Windows/onnx_private_files.cmake
  24. 15 0
      Gems/ONNX/Code/Platform/Windows/onnx_shared_files.cmake
  25. 11 0
      Gems/ONNX/Code/Platform/iOS/PAL_ios.cmake
  26. 10 0
      Gems/ONNX/Code/Platform/iOS/onnx_api_files.cmake
  27. 15 0
      Gems/ONNX/Code/Platform/iOS/onnx_private_files.cmake
  28. 15 0
      Gems/ONNX/Code/Platform/iOS/onnx_shared_files.cmake
  29. 166 0
      Gems/ONNX/Code/Source/Clients/Mnist.cpp
  30. 75 0
      Gems/ONNX/Code/Source/Clients/Mnist.h
  31. 153 0
      Gems/ONNX/Code/Source/Clients/Model.cpp
  32. 23 0
      Gems/ONNX/Code/Source/Clients/ONNXModule.cpp
  33. 135 0
      Gems/ONNX/Code/Source/Clients/ONNXSystemComponent.cpp
  34. 65 0
      Gems/ONNX/Code/Source/Clients/ONNXSystemComponent.h
  35. 43 0
      Gems/ONNX/Code/Source/ONNXModuleInterface.h
  36. 45 0
      Gems/ONNX/Code/Source/Tools/ONNXEditorModule.cpp
  37. 62 0
      Gems/ONNX/Code/Source/Tools/ONNXEditorSystemComponent.cpp
  38. 39 0
      Gems/ONNX/Code/Source/Tools/ONNXEditorSystemComponent.h
  39. 105 0
      Gems/ONNX/Code/Tests/MnistTests.cpp
  40. 12 0
      Gems/ONNX/Code/onnx_api_files.cmake
  41. 10 0
      Gems/ONNX/Code/onnx_editor_api_files.cmake
  42. 12 0
      Gems/ONNX/Code/onnx_editor_private_files.cmake
  43. 11 0
      Gems/ONNX/Code/onnx_editor_shared_files.cmake
  44. 10 0
      Gems/ONNX/Code/onnx_editor_tests_files.cmake
  45. 15 0
      Gems/ONNX/Code/onnx_private_files.cmake
  46. 16 0
      Gems/ONNX/Code/onnx_shared_files.cmake
  47. 15 0
      Gems/ONNX/Code/onnx_tests_files.cmake
  48. 19 0
      Gems/ONNX/External/FindOnnx.cmake
  49. 40 0
      Gems/ONNX/README.md
  50. 18 0
      Gems/ONNX/Registry/assetprocessor_settings.setreg
  51. 20 0
      Gems/ONNX/gem.json
  52. 3 0
      Gems/ONNX/preview.png

+ 0 - 0
Gems/ONNX/.gitignore


+ 18 - 0
Gems/ONNX/CMakeLists.txt

@@ -0,0 +1,18 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(gem_path ${CMAKE_CURRENT_LIST_DIR})
+set(gem_json ${gem_path}/gem.json)
+
+o3de_restricted_path(${gem_json} gem_restricted_path gem_parent_relative_path)
+
+o3de_pal_dir(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} "${gem_restricted_path}" "${gem_path}" "${gem_parent_relative_path}")
+
+ly_add_external_target_path(${CMAKE_CURRENT_LIST_DIR}/External)
+
+add_subdirectory(Code)

+ 254 - 0
Gems/ONNX/Code/CMakeLists.txt

@@ -0,0 +1,254 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+o3de_pal_dir(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} "${gem_restricted_path}" "${gem_path}" "${gem_parent_relative_path}")
+include(${pal_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
+
+find_package(Onnx REQUIRED)
+
+if(NOT PAL_TRAIT_ONNX_SUPPORTED OR NOT Onnx_FOUND)
+    # Don't create any Gem targets and aliases.  Nothing should depend on this
+    # Gem directly, because if it doesn't define targets it will cause an error.
+    return()
+endif()
+
+# The ONNX.API target declares the common interface that users of this gem should depend on in their targets
+ly_add_target(
+    NAME ONNX.API INTERFACE
+    NAMESPACE Gem
+    FILES_CMAKE
+        onnx_api_files.cmake
+        ${pal_dir}/onnx_api_files.cmake
+    INCLUDE_DIRECTORIES
+        INTERFACE
+            Include
+            Source
+            ${Onnx_INCLUDE_DIR}
+    BUILD_DEPENDENCIES
+        INTERFACE
+           AZ::AzCore
+           AZ::AzFramework
+           AZ::AzToolsFramework
+           Gem::ImguiAtom.Static
+           Gem::ImGui.Static
+           Gem::ImGui.ImGuiLYUtils
+           ${Onnx_LIB}
+)
+
+# The ONNX.Private.Object target is an internal target
+# It should not be used outside of this Gems CMakeLists.txt
+ly_add_target(
+    NAME ONNX.Private.Object STATIC
+    NAMESPACE Gem
+    FILES_CMAKE
+        onnx_private_files.cmake
+        ${pal_dir}/onnx_private_files.cmake
+    COMPILE_DEFINITIONS
+        PUBLIC ENABLE_CUDA=true
+    TARGET_PROPERTIES
+        O3DE_PRIVATE_TARGET TRUE
+    INCLUDE_DIRECTORIES
+        PUBLIC
+            Include
+            Source
+            ${Onnx_INCLUDE_DIR}
+    BUILD_DEPENDENCIES
+        PUBLIC
+            AZ::AzCore
+            AZ::AzFramework
+            AZ::AzToolsFramework
+            ${Onnx_LIB}
+            Gem::ImguiAtom.Static
+            Gem::ImGui.Static
+            Gem::ImGui.ImGuiLYUtils
+)
+
+# Here add ONNX target, it depends on the Private Object library and Public API interface
+ly_add_target(
+    NAME ONNX ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE}
+    NAMESPACE Gem
+    FILES_CMAKE
+        onnx_shared_files.cmake
+        ${pal_dir}/onnx_shared_files.cmake
+    INCLUDE_DIRECTORIES
+        PUBLIC
+            Include
+        PRIVATE
+            Source
+    BUILD_DEPENDENCIES
+        PUBLIC
+            Gem::ONNX.API
+            ${Onnx_LIB}
+        PRIVATE
+            Gem::ONNX.Private.Object
+        INTERFACE
+            Gem::ImguiAtom.Static
+            Gem::ImGui.Static
+            Gem::ImGui.ImGuiLYUtils
+)
+
+# By default, we will specify that the above target ONNX would be used by
+# Client and Server type targets when this gem is enabled.  If you don't want it
+# active in Clients or Servers by default, delete one of both of the following lines:
+ly_create_alias(NAME ONNX.Clients NAMESPACE Gem TARGETS Gem::ONNX)
+ly_create_alias(NAME ONNX.Servers NAMESPACE Gem TARGETS Gem::ONNX)
+
+# For the Client and Server variants of ONNX Gem, an alias to the ONNX.API target will be made
+ly_create_alias(NAME ONNX.Clients.API NAMESPACE Gem TARGETS Gem::ONNX.API)
+ly_create_alias(NAME ONNX.Servers.API NAMESPACE Gem TARGETS Gem::ONNX.API)
+
+# If we are on a host platform, we want to add the host tools targets like the ONNX.Editor MODULE target
+if(PAL_TRAIT_BUILD_HOST_TOOLS)
+    # The ONNX.Editor.API target can be used by other gems that want to interact with the ONNX.Editor module
+    ly_add_target(
+        NAME ONNX.Editor.API INTERFACE
+        NAMESPACE Gem
+        FILES_CMAKE
+            onnx_editor_api_files.cmake
+            ${pal_dir}/onnx_editor_api_files.cmake
+        INCLUDE_DIRECTORIES
+            INTERFACE
+                Include
+        BUILD_DEPENDENCIES
+            INTERFACE
+                AZ::AzToolsFramework
+                Gem::ImguiAtom.Static
+                Gem::ImGui.Static
+                Gem::ImGui.ImGuiLYUtils
+    )
+
+    # The ONNX.Editor.Private.Object target is an internal target
+    # which is only to be used by this gems CMakeLists.txt and any subdirectories
+    # Other gems should not use this target
+    ly_add_target(
+        NAME ONNX.Editor.Private.Object STATIC
+        NAMESPACE Gem
+        FILES_CMAKE
+            onnx_editor_private_files.cmake
+        TARGET_PROPERTIES
+            O3DE_PRIVATE_TARGET TRUE
+        INCLUDE_DIRECTORIES
+            PRIVATE
+                Include
+                Source
+        BUILD_DEPENDENCIES
+            PUBLIC
+                AZ::AzToolsFramework
+                $<TARGET_OBJECTS:Gem::ONNX.Private.Object>
+                ${Onnx_LIB}
+                Gem::ImGui.Static
+                Gem::ImGui.ImGuiLYUtils
+            INTERFACE
+                Gem::ImguiAtom.Static
+    )
+
+    ly_add_target(
+        NAME ONNX.Editor GEM_MODULE
+        NAMESPACE Gem
+        AUTOMOC
+        FILES_CMAKE
+            onnx_editor_shared_files.cmake
+        INCLUDE_DIRECTORIES
+            PRIVATE
+                Source
+            PUBLIC
+                Include
+        BUILD_DEPENDENCIES
+            PUBLIC
+                Gem::ONNX.Editor.API
+                ${Onnx_LIB}
+            PRIVATE
+                Gem::ONNX.Editor.Private.Object
+            INTERFACE
+                Gem::ImguiAtom.Static
+                Gem::ImGui.Static
+                Gem::ImGui.ImGuiLYUtils
+    )
+
+    # By default, we will specify that the above target ONNX would be used by
+    # Tool and Builder type targets when this gem is enabled.  If you don't want it
+    # active in Tools or Builders by default, delete one of both of the following lines:
+    ly_create_alias(NAME ONNX.Tools    NAMESPACE Gem TARGETS Gem::ONNX.Editor)
+    ly_create_alias(NAME ONNX.Builders NAMESPACE Gem TARGETS Gem::ONNX.Editor)
+
+    # For the Tools and Builders variants of ONNX Gem, an alias to the ONNX.Editor API target will be made
+    ly_create_alias(NAME ONNX.Tools.API NAMESPACE Gem TARGETS Gem::ONNX.Editor.API)
+    ly_create_alias(NAME ONNX.Builders.API NAMESPACE Gem TARGETS Gem::ONNX.Editor.API)
+
+endif()
+
+################################################################################
+# Tests
+################################################################################
+# See if globally, tests are supported
+if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
+    # We globally support tests, see if we support tests on this platform for ONNX.Tests
+    if(PAL_TRAIT_ONNX_TEST_SUPPORTED)
+        # We support ONNX.Tests on this platform, add dependency on the Private Object target
+        ly_add_target(
+            NAME ONNX.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
+            NAMESPACE Gem
+            FILES_CMAKE
+                onnx_private_files.cmake
+                onnx_tests_files.cmake
+            TARGET_PROPERTIES
+                O3DE_PRIVATE_TARGET TRUE
+            INCLUDE_DIRECTORIES
+                PUBLIC
+                    Tests
+                    Source
+                    Source/Clients
+                    ${Onnx_INCLUDE_DIR}
+                    Include
+            BUILD_DEPENDENCIES
+                PRIVATE
+                    AZ::AzTest
+                    AZ::AzFramework
+                    AZ::AzToolsFrameworkTestCommon
+                    Gem::ONNX.Private.Object
+                PUBLIC
+                    ${Onnx_LIB}
+                    Gem::ONNX.API
+        )
+
+        # Add ONNX.Tests to googletest
+        ly_add_googletest(
+            NAME Gem::ONNX.Tests
+        )
+    endif()
+
+    # If we are a host platform we want to add tools test like editor tests here
+    if(PAL_TRAIT_BUILD_HOST_TOOLS)
+        # We are a host platform, see if Editor tests are supported on this platform
+        if(PAL_TRAIT_ONNX_EDITOR_TEST_SUPPORTED)
+            # We support ONNX.Editor.Tests on this platform, add ONNX.Editor.Tests target which depends on
+            # private ONNX.Editor.Private.Object target
+            ly_add_target(
+                NAME ONNX.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
+                NAMESPACE Gem
+                FILES_CMAKE
+                    onnx_editor_tests_files.cmake
+                INCLUDE_DIRECTORIES
+                    PRIVATE
+                        Tests
+                        Source
+                BUILD_DEPENDENCIES
+                    PRIVATE
+                        AZ::AzTest
+                        Gem::ONNX.Private.Object
+                    PUBLIC
+                        ${Onnx_LIB}
+            )
+
+            # Add ONNX.Editor.Tests to googletest
+            ly_add_googletest(
+                NAME Gem::ONNX.Editor.Tests
+            )
+        endif()
+    endif()
+endif()

+ 70 - 0
Gems/ONNX/Code/Include/ONNX/Model.h

@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/Component/Component.h>
+#include <AzCore/Component/TickBus.h>
+#include <AzCore/Debug/Timer.h>
+#include <AzCore/IO/FileIO.h>
+#include <AzCore/Math/Color.h>
+#include <AzCore/std/string/conversions.h>
+#include <AzFramework/StringFunc/StringFunc.h>
+#include <ONNX/ONNXBus.h>
+#include <onnxruntime_cxx_api.h>
+
+namespace ONNX
+{
+    //! Generic ONNX model which can be used to create an inference session and run inferences.
+    class Model
+    {
+    public:
+        Model() = default;
+
+        //! Required params to create session and run inference, passed to Load().
+        struct InitSettings
+        {
+            //! Source of onnx model file.
+            AZ::IO::FixedMaxPath m_modelFile;
+            AZStd::string m_modelName = ""; //!< Used to create groupings for ImGui dashboard graphs in editor, idea is that the inference runtimes from the same model instance get displayed on the same graph.
+            AZ::Color m_modelColor = AZ::Color::CreateFromRgba(229, 56, 59, 255); //!< Sets the color of the dashboard graph for runtime statistics of this model instance.
+            bool m_cudaEnable = false; //!< Toggle to create a CUDA session on gpu, if disabled normal cpu session created.
+        };
+
+        //! Initialises necessary params in order to run inference.
+        //! Must be executed before Run().
+        //! Creates the session, memory info, and extracts input and output names, shapes and count from onnx model file.
+        //! Creates vector m_outputs that holds inference outputs based on onnx model file's output counts and shapes for each output
+        //! ie if onnx model has output count of 2 each with shapes of {1, 1, 28, 28} then m_outputs will hold 2 vectors of length 784 each.
+        //! Only needs to be executed once, inferences using the same onnx model file can be run by providing different input/output params to
+        //! Run().
+        void Load(const InitSettings& initSettings);
+
+        //! Executes the inference using the loaded model.
+        //! Input and output vectors are used to generate their respective tensors.
+        //! Output is mutated directly.
+        void Run(AZStd::vector<AZStd::vector<float>>& inputs);
+
+        float m_delta = 0.0f; //!< Runtime in ms of latest inference.
+        AZStd::vector<AZStd::vector<float>> m_outputs; //!< Outputs of latest inference (initialized by Load() - size and shape extracted from onnx model file).
+
+    protected:
+        bool m_cudaEnable = false; // Determines if inferencing of the model instance will be run on gpu using CUDA (run on CPU by default).
+        AZStd::string m_modelName; // Used to create groupings for ImGui dashboard graphs in editor, idea is that the inference runtimes from the same model instance get displayed on the same graph.
+        AZ::Color m_modelColor = AZ::Color::CreateFromRgba(229, 56, 59, 255); // Sets the color of the dashboard graph for runtime statistics of this model instance.
+        AZ::Debug::Timer m_timer; // Timer instance that is used within Run() to calculate inference runtime, and obtain the value in m_delta.
+        Ort::MemoryInfo m_memoryInfo{ nullptr }; // Created by Load() and holds information about the memory allocator used by the instance and the memory type. These are set to OrtDeviceAllocator and OrtMemTypeCpu for both CPU and GPU execution (contrary to how it may seem this is the correct MemType for CUDA as well).
+        Ort::Session m_session{ nullptr }; // Created by Load(), and is unique to the model.onnx file used - created using the Ort::Env and SessionOptions which are used to specify CPU or CUDA execution.
+        AZStd::vector<AZStd::vector<int64_t>> m_inputShapes; // A vector of the dimensions of input, eg vectors specifying dimension and magnitude of dimension such as { 1, 1, 28, 28 }.
+        size_t m_inputCount = 0; // The number of inputs in the model.onnx file. Corresponds with the number of input names.
+        AZStd::vector<const char*> m_inputNames; // A vector of the input names extracted from the model.onnx file.
+        AZStd::vector<AZStd::vector<int64_t>> m_outputShapes; // A vector of the dimensions of output, eg vectors specifying dimension and magnitude of dimension such as { 1, 10 }.
+        size_t m_outputCount = 0; // The number of outputs in the model.onnx file. Corresponds with the number of output names.
+        AZStd::vector<const char*> m_outputNames; // A vector of the output names extracted from the model.onnx file.
+    };
+} // namespace ONNX

+ 47 - 0
Gems/ONNX/Code/Include/ONNX/ONNXBus.h

@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/EBus/EBus.h>
+#include <AzCore/Interface/Interface.h>
+
+namespace Ort
+{
+    struct Env;
+    struct AllocatorWithDefaultOptions;
+} // namespace Ort
+
+namespace ONNX
+{
+    class ONNXRequests
+    {
+    public:
+        AZ_RTTI(ONNXRequests, "{F8599C7E-CDC7-4A72-A296-2C043D1E525A}");
+        virtual ~ONNXRequests() = default;
+
+        virtual Ort::Env* GetEnv() = 0;
+        virtual Ort::AllocatorWithDefaultOptions* GetAllocator() = 0;
+
+        virtual void AddTimingSample(const char* modelName, float inferenceTimeInMilliseconds, AZ::Color modelColor) = 0;
+    };
+
+    class ONNXBusTraits : public AZ::EBusTraits
+    {
+    public:
+        //////////////////////////////////////////////////////////////////////////
+        // EBusTraits overrides
+        static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
+        static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
+        //////////////////////////////////////////////////////////////////////////
+    };
+
+    using ONNXRequestBus = AZ::EBus<ONNXRequests, ONNXBusTraits>;
+    using ONNXInterface = AZ::Interface<ONNXRequests>;
+
+} // namespace ONNX

+ 11 - 0
Gems/ONNX/Code/Platform/Android/PAL_android.cmake

@@ -0,0 +1,11 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_ONNX_SUPPORTED FALSE)
+set(PAL_TRAIT_ONNX_TEST_SUPPORTED FALSE)
+set(PAL_TRAIT_ONNX_EDITOR_TEST_SUPPORTED FALSE)

+ 10 - 0
Gems/ONNX/Code/Platform/Android/onnx_api_files.cmake

@@ -0,0 +1,10 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+)

+ 15 - 0
Gems/ONNX/Code/Platform/Android/onnx_private_files.cmake

@@ -0,0 +1,15 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+# Platform specific files for Android
+# i.e. ../Source/Android/ONNXAndroid.cpp
+#      ../Source/Android/ONNXAndroid.h
+#      ../Include/Android/ONNXAndroid.h
+
+set(FILES
+)

+ 15 - 0
Gems/ONNX/Code/Platform/Android/onnx_shared_files.cmake

@@ -0,0 +1,15 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+# Platform specific files for Android
+# i.e. ../Source/Android/ONNXAndroid.cpp
+#      ../Source/Android/ONNXAndroid.h
+#      ../Include/Android/ONNXAndroid.h
+
+set(FILES
+)

+ 12 - 0
Gems/ONNX/Code/Platform/Linux/PAL_linux.cmake

@@ -0,0 +1,12 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_ONNX_SUPPORTED TRUE)
+set(PAL_TRAIT_ONNX_TEST_SUPPORTED TRUE)
+set(PAL_TRAIT_ONNX_EDITOR_TEST_SUPPORTED TRUE)
+set(ONNXRUNTIME_DEPENDENCY "libonnxruntime.so")

+ 10 - 0
Gems/ONNX/Code/Platform/Linux/onnx_api_files.cmake

@@ -0,0 +1,10 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+)

+ 10 - 0
Gems/ONNX/Code/Platform/Linux/onnx_editor_api_files.cmake

@@ -0,0 +1,10 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+)

+ 15 - 0
Gems/ONNX/Code/Platform/Linux/onnx_private_files.cmake

@@ -0,0 +1,15 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+# Platform specific files for Linux
+# i.e. ../Source/Linux/ONNXLinux.cpp
+#      ../Source/Linux/ONNXLinux.h
+#      ../Include/Linux/ONNXLinux.h
+
+set(FILES
+)

+ 15 - 0
Gems/ONNX/Code/Platform/Linux/onnx_shared_files.cmake

@@ -0,0 +1,15 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+# Platform specific files for Linux
+# i.e. ../Source/Linux/ONNXLinux.cpp
+#      ../Source/Linux/ONNXLinux.h
+#      ../Include/Linux/ONNXLinux.h
+
+set(FILES
+)

+ 11 - 0
Gems/ONNX/Code/Platform/Mac/PAL_mac.cmake

@@ -0,0 +1,11 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_ONNX_SUPPORTED FALSE)
+set(PAL_TRAIT_ONNX_TEST_SUPPORTED FALSE)
+set(PAL_TRAIT_ONNX_EDITOR_TEST_SUPPORTED FALSE)

+ 10 - 0
Gems/ONNX/Code/Platform/Mac/onnx_api_files.cmake

@@ -0,0 +1,10 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+)

+ 10 - 0
Gems/ONNX/Code/Platform/Mac/onnx_editor_api_files.cmake

@@ -0,0 +1,10 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+)

+ 15 - 0
Gems/ONNX/Code/Platform/Mac/onnx_private_files.cmake

@@ -0,0 +1,15 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+# Platform specific files for Mac
+# i.e. ../Source/Mac/ONNXMac.cpp
+#      ../Source/Mac/ONNXMac.h
+#      ../Include/Mac/ONNXMac.h
+
+set(FILES
+)

+ 15 - 0
Gems/ONNX/Code/Platform/Mac/onnx_shared_files.cmake

@@ -0,0 +1,15 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+# Platform specific files for Mac
+# i.e. ../Source/Mac/ONNXMac.cpp
+#      ../Source/Mac/ONNXMac.h
+#      ../Include/Mac/ONNXMac.h
+
+set(FILES
+)

+ 12 - 0
Gems/ONNX/Code/Platform/Windows/PAL_windows.cmake

@@ -0,0 +1,12 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_ONNX_SUPPORTED TRUE)
+set(PAL_TRAIT_ONNX_TEST_SUPPORTED TRUE)
+set(PAL_TRAIT_ONNX_EDITOR_TEST_SUPPORTED TRUE)
+set(ONNXRUNTIME_DEPENDENCY "onnxruntime.lib")

+ 10 - 0
Gems/ONNX/Code/Platform/Windows/onnx_api_files.cmake

@@ -0,0 +1,10 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+)

+ 10 - 0
Gems/ONNX/Code/Platform/Windows/onnx_editor_api_files.cmake

@@ -0,0 +1,10 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+)

+ 15 - 0
Gems/ONNX/Code/Platform/Windows/onnx_private_files.cmake

@@ -0,0 +1,15 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+# Platform specific files for Windows
+# i.e. ../Source/Windows/ONNXWindows.cpp
+#      ../Source/Windows/ONNXWindows.h
+#      ../Include/Windows/ONNXWindows.h
+
+set(FILES
+)

+ 15 - 0
Gems/ONNX/Code/Platform/Windows/onnx_shared_files.cmake

@@ -0,0 +1,15 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+# Platform specific files for Windows
+# i.e. ../Source/Windows/ONNXWindows.cpp
+#      ../Source/Windows/ONNXWindows.h
+#      ../Include/Windows/ONNXWindows.h
+
+set(FILES
+)

+ 11 - 0
Gems/ONNX/Code/Platform/iOS/PAL_ios.cmake

@@ -0,0 +1,11 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_ONNX_SUPPORTED FALSE)
+set(PAL_TRAIT_ONNX_TEST_SUPPORTED FALSE)
+set(PAL_TRAIT_ONNX_EDITOR_TEST_SUPPORTED FALSE)

+ 10 - 0
Gems/ONNX/Code/Platform/iOS/onnx_api_files.cmake

@@ -0,0 +1,10 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+)

+ 15 - 0
Gems/ONNX/Code/Platform/iOS/onnx_private_files.cmake

@@ -0,0 +1,15 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+# Platform specific files for iOS
+# i.e. ../Source/iOS/ONNXiOS.cpp
+#      ../Source/iOS/ONNXiOS.h
+#      ../Include/iOS/ONNXiOS.h
+
+set(FILES
+)

+ 15 - 0
Gems/ONNX/Code/Platform/iOS/onnx_shared_files.cmake

@@ -0,0 +1,15 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+# Platform specific files for iOS
+# i.e. ../Source/iOS/ONNXiOS.cpp
+#      ../Source/iOS/ONNXiOS.h
+#      ../Include/iOS/ONNXiOS.h
+
+set(FILES
+)

+ 166 - 0
Gems/ONNX/Code/Source/Clients/Mnist.cpp

@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include "Mnist.h"
+
+namespace Mnist
+{
+    template<typename T>
+    static void Mnist::Softmax(T& input)
+    {
+        const float rowmax = *AZStd::ranges::max_element(input.begin(), input.end());
+        AZStd::vector<float> y(input.size());
+        float sum = 0.0f;
+        for (size_t i = 0; i != input.size(); ++i)
+        {
+            sum += y[i] = AZStd::exp2(input[i] - rowmax);
+        }
+        for (size_t i = 0; i != input.size(); ++i)
+        {
+            input[i] = y[i] / sum;
+        }
+    }
+
+    void Mnist::GetResult()
+    {
+        Softmax(m_outputs[0]);
+        m_result = AZStd::distance(m_outputs[0].begin(), AZStd::ranges::max_element(m_outputs[0].begin(), m_outputs[0].end()));
+    }
+
+    void Mnist::LoadImage(const char* path)
+    {
+        // Gets the png image from file and decodes using upng library.
+        upng_t* upng = upng_new_from_file(path);
+        upng_decode(upng);
+        const unsigned char* buffer = upng_get_buffer(upng);
+
+        // Converts image from buffer into binary greyscale representation.
+        // i.e. a pure black pixel is a 0, anything else is a 1.
+        // Bear in mind that the images in the dataset are flipped compared to how we'd usually think,
+        // so the background is black and the actual digit is white.
+        for (int y = 0; y < m_imageHeight; y++)
+        {
+            for (int x = 0; x < m_imageWidth; x++)
+            {
+                int content = static_cast<int>(buffer[(y)*m_imageWidth + x]);
+                if (content == 0)
+                {
+                    m_input[0][m_imageWidth * y + x] = 0.0f;
+                }
+                else
+                {
+                    m_input[0][m_imageHeight * y + x] = 1.0f;
+                }
+            }
+        }
+    }
+
+    MnistReturnValues MnistExample(Mnist& mnist, const char* path)
+    {
+        mnist.LoadImage(path);
+        mnist.Run(mnist.m_input);
+        mnist.GetResult();
+
+        MnistReturnValues returnValues;
+        returnValues.m_inference = mnist.m_result;
+        returnValues.m_runtime = mnist.m_delta;
+        return (returnValues);
+    }
+
+    InferenceData RunMnistSuite(int testsPerDigit, bool cudaEnable)
+    {
+        // Initialises and loads the mnist model.
+        // The same instance of the model is used for all runs.
+        Mnist mnist;
+        AZStd::vector<AZStd::vector<float>> input(0);
+        AZStd::vector<float> image(mnist.m_imageSize);
+        input.push_back(image);
+        mnist.m_input = input;
+
+        Mnist::InitSettings modelInitSettings;
+
+        if (cudaEnable)
+        {
+            modelInitSettings.m_modelName = "MNIST CUDA (Precomputed)";
+            modelInitSettings.m_modelColor = AZ::Color::CreateFromRgba(56, 229, 59, 255);
+            modelInitSettings.m_cudaEnable = true;
+        }
+        else
+        {
+            modelInitSettings.m_modelName = "MNIST (Precomputed)";
+        }
+
+        mnist.Load(modelInitSettings);
+
+        int numOfEach = testsPerDigit;
+        int totalFiles = 0;
+        int64_t numOfCorrectInferences = 0;
+        float totalRuntimeInMilliseconds = 0;
+        AZ::IO::FixedMaxPath mnistTestImageRoot;
+
+        // This bit cycles through the folder with all the mnist test images, calling MnistExample() for the specified number of each digit.
+        // The structure of the folder is as such: /testing/{digit}/{random_integer}.png    e.g /testing/3/10.png
+        auto TestImage = [&mnist, &numOfCorrectInferences, &totalFiles, &totalRuntimeInMilliseconds, &mnistTestImageRoot, numOfEach](AZ::IO::Path digitFilePath, bool isFile) -> bool
+        {
+            if (!isFile)
+            {
+                AZ::IO::FixedMaxPath directoryName = digitFilePath.Filename();
+                char* directoryEnd;
+                AZ::s64 digit = strtoll(directoryName.c_str(), &directoryEnd, 10);
+                if (directoryName.c_str() != directoryEnd)
+                {
+                    // How many files of that digit have been tested
+                    int version = 0;
+
+                    // Search for any png files
+                    auto RunMnistExample = [&mnist, &numOfCorrectInferences, &totalFiles, &totalRuntimeInMilliseconds, &mnistTestImageRoot, &directoryName, &version, &digit, &numOfEach](AZ::IO::Path pngFilePath, bool) -> bool
+                    {
+                        // Stop running examples once version limit for that digit has been reached
+                        if ((version < numOfEach))
+                        {
+                            MnistReturnValues returnedValues = MnistExample(mnist, (mnistTestImageRoot / directoryName / pngFilePath).c_str());
+                            if (returnedValues.m_inference == digit)
+                            {
+                                numOfCorrectInferences += 1;
+                            }
+                            totalRuntimeInMilliseconds += returnedValues.m_runtime;
+                            totalFiles++;
+                            version++;
+                        }
+                        return true;
+                    };
+                    AZ::IO::SystemFile::FindFiles((mnistTestImageRoot / directoryName / "*.png").c_str(), RunMnistExample);
+                }
+            }
+            return true;
+        };
+
+        // Get the FileIOBase to resolve the path to the MNIST testing image folder in the onnx gem
+        AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance();
+        if (fileIo->ResolvePath(mnistTestImageRoot, "@gemroot:ONNX@/Assets/mnist_png/testing"))
+        {
+            // mnistTestImageRoot is set to the root folder of the MNIST testing images
+            AZ::IO::SystemFile::FindFiles((mnistTestImageRoot / "*").c_str(), TestImage);
+        }
+
+        float accuracy = ((float)numOfCorrectInferences / (float)totalFiles) * 100.0f;
+        float avgRuntimeInMilliseconds = totalRuntimeInMilliseconds / (totalFiles);
+
+        AZ_Printf("ONNX", " Run Type: %s\n", cudaEnable ? "CUDA" : "CPU");
+        AZ_Printf("ONNX", " Evaluated: %d  Correct: %d  Accuracy: %f%%\n", totalFiles, numOfCorrectInferences, accuracy);
+        AZ_Printf("ONNX", " Total Runtime: %fms  Avg Runtime: %fms\n", totalRuntimeInMilliseconds, avgRuntimeInMilliseconds);
+
+        InferenceData result;
+        result.m_averageRuntimeInMs = avgRuntimeInMilliseconds;
+        result.m_totalRuntimeInMs = totalRuntimeInMilliseconds;
+        result.m_totalNumberOfInferences = totalFiles;
+        result.m_numberOfCorrectInferences = numOfCorrectInferences;
+
+        return result;
+    }
+} // namespace Mnist

+ 75 - 0
Gems/ONNX/Code/Source/Clients/Mnist.h

@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/IO/SystemFile.h>
+
+#include <ONNX/Model.h>
+#include "upng/upng.h"
+
+namespace Mnist
+{
+    //! Holds the digit that was inferenced and the time taken for a single inference run.
+    //! Only used by MnistExample().
+    struct MnistReturnValues
+    {
+        int64_t m_inference = 0;
+        float m_runtime = 0.0f;
+    };
+
+    //! Holds the data gathered from RunMnistSuite(), which tests the MNIST ONNX model against images from the MNIST dataset.
+    struct InferenceData
+    {
+        float m_totalRuntimeInMs = 0.0f;
+        float m_averageRuntimeInMs = 0.0f;
+        int m_totalNumberOfInferences = 0;
+        int64_t m_numberOfCorrectInferences = 0;
+    };
+
+    //! Extension of ONNX Model used for Mnist example.
+    //! Implements additional functionality useful to have for the example, such as keeping hold of the input and output vectors, and result
+    //! (which the model doesn't do).
+    struct Mnist
+        : public ::ONNX::Model
+    {
+    public:
+        //! Loads an image from file into the correct format in m_input.
+        //! @path is the file location of the image you want to inference (this NEEDS to be an 8-bit color depth png else it won't work).
+        void LoadImage(const char* path);
+
+        //! To be called after Model::Run(), uses softmax to get inference probabilities.
+        //! Directly mutates m_output and m_result.
+        void GetResult();
+
+        //! The MNIST dataset images are all 28 x 28 px, so you should probably be loading 28 x 28 images into the example.
+        int m_imageWidth = 28;
+        int m_imageHeight = 28;
+        int m_imageSize = 784;
+
+        AZStd::vector<AZStd::vector<float>> m_input; //!< This is the input that gets passed into Run(). A binary representation of the pixels in the image.
+        int64_t m_result{ 0 }; //!< This will be the digit with the highest probability from the inference (what the model thinks the input number was).
+
+    private:
+        // Converts vector of output values into vector of probabilities.
+        template<typename T>
+        static void Softmax(T& input);
+    };
+
+    //! This will run a single inference on the passed in MNIST instance.
+    //! @mnist should be in a ready to run state, ie Load() should have been called.
+    //! @path is the file location of the image you want to inference (this NEEDS to be an 8-bit color depth png else it won't work).
+    //! Returns the inference digit and runtime.
+    MnistReturnValues MnistExample(Mnist& mnist, const char* path);
+
+    //! Runs through library of test mnist images in png format, calculating inference accuracy.
+    //! @testsPerDigit specifies how many runs to do on each digit 0-9. Each run will be done on a unique image of that digit. Limit is
+    //! ~9,000.
+    //! @cudaEnable just specifies if the inferences should be run on gpu using CUDA or default cpu.
+    InferenceData RunMnistSuite(int testsPerDigit, bool cudaEnable);
+} // namespace Mnist

+ 153 - 0
Gems/ONNX/Code/Source/Clients/Model.cpp

@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <ONNX/Model.h>
+
+namespace ONNX
+{
+    void Model::Load(const InitSettings& initSettings)
+    {
+        // Get the FileIOBase to resolve the path to the ONNX gem
+        AZ::IO::FixedMaxPath onnxModelPath;
+
+        // If no filepath provided for onnx model, set default to a model.onnx file in the Assets folder.
+        if (initSettings.m_modelFile.empty())
+        {
+            AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance();
+            fileIo->ResolvePath(onnxModelPath, "@gemroot:ONNX@/Assets/model.onnx");
+        }
+        else
+        {
+            onnxModelPath = initSettings.m_modelFile;
+        }
+
+        // If no model name is provided, will default to the name of the onnx model file.
+        if (initSettings.m_modelName.empty())
+        {
+            m_modelName = onnxModelPath.Filename().Stem().FixedMaxPathString();
+        }
+        else
+        {
+            m_modelName = initSettings.m_modelName;
+        }
+
+        m_modelColor = initSettings.m_modelColor;
+
+        // Grabs environment created on init of system component.
+        Ort::Env* env = nullptr;
+        ONNXRequestBus::BroadcastResult(env, &ONNXRequestBus::Events::GetEnv);
+
+#ifdef ENABLE_CUDA
+        // OrtCudaProviderOptions must be added to the session options to specify execution on CUDA.
+        // Can specify a number of parameters about the CUDA execution here - currently all left at default.
+        Ort::SessionOptions sessionOptions;
+        if (initSettings.m_cudaEnable)
+        {
+            OrtCUDAProviderOptions cuda_options;
+            sessionOptions.AppendExecutionProvider_CUDA(cuda_options);
+        }
+        m_cudaEnable = initSettings.m_cudaEnable;
+#endif
+
+        // The model_path provided to Ort::Session needs to be const wchar_t*, even though the docs state const char* - doesn't work otherwise.
+        AZStd::string onnxModelPathString = onnxModelPath.String();
+        m_session = Ort::Session(*env, AZStd::wstring(onnxModelPathString.cbegin(), onnxModelPathString.cend()).c_str(), sessionOptions);
+        m_memoryInfo = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
+
+        // Grabs memory allocator created on init of system component.
+        Ort::AllocatorWithDefaultOptions* m_allocator;
+        ONNXRequestBus::BroadcastResult(m_allocator, &ONNXRequestBus::Events::GetAllocator);
+
+        // Extract input names from model file and put into const char* vectors.
+        // Extract input shapes from model file and put into AZStd::vector<int64_t>.
+        m_inputCount = m_session.GetInputCount();
+        for (size_t i = 0; i < m_inputCount; i++)
+        {
+            const char* inName = m_session.GetInputName(i, *m_allocator);
+            m_inputNames.push_back(inName);
+
+            std::vector<int64_t> inputShape = m_session.GetInputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape();
+            AZStd::vector<int64_t> azInputShape(inputShape.begin(), inputShape.end());
+            for (int index = 0; index < azInputShape.size(); index++)
+            {
+                if (azInputShape[index] == -1)
+                {
+                    azInputShape[index] = 1;
+                }
+            }
+            m_inputShapes.push_back(azInputShape);
+        }
+
+        // Extract output names from model file and put into const char* vectors.
+        // Extract output shapes from model file and put into AZStd::vector<int64_t>.
+        // Initialize m_outputs vector using output shape and count.
+        m_outputCount = m_session.GetOutputCount();
+        AZStd::vector<AZStd::vector<float>> outputs(m_outputCount);
+        for (size_t i = 0; i < m_outputCount; i++)
+        {
+            const char* outName = m_session.GetOutputName(i, *m_allocator);
+            m_outputNames.push_back(outName);
+
+            std::vector<int64_t> outputShape = m_session.GetOutputTypeInfo(0).GetTensorTypeAndShapeInfo().GetShape();
+            AZStd::vector<int64_t> azOutputShape(outputShape.begin(), outputShape.end());
+            for (int index = 0; index < azOutputShape.size(); index++)
+            {
+                if (azOutputShape[index] == -1)
+                {
+                    azOutputShape[index] = 1;
+                }
+            }
+            m_outputShapes.push_back(azOutputShape);
+
+            int64_t outputSize = 1;
+            for (int j = 0; j < m_outputShapes[i].size(); j++)
+            {
+                // The size of each output is simply all the magnitudes of the shape dimensions multiplied together.
+                if (m_outputShapes[i][j] > 0)
+                {
+                    outputSize *= m_outputShapes[i][j];
+                }
+            }
+            AZStd::vector<float> output(outputSize);
+            outputs[i] = output;
+        }
+        m_outputs = outputs;
+    }
+
+    void Model::Run(AZStd::vector<AZStd::vector<float>>& inputs)
+    {
+        m_timer.Stamp(); // Start timing of inference.
+
+        // Tensor creation is lightweight, and a tensor is just a wrapper around the memory owned by the vector passed in as data during creation.
+        // As such, creating input and output tensors in each run call does not adversely affect performance.
+        AZStd::vector<Ort::Value> inputTensors;
+        for (int i = 0; i < m_inputCount; i++)
+        {
+            Ort::Value inputTensor =
+                Ort::Value::CreateTensor<float>(m_memoryInfo, inputs[i].data(), inputs[i].size(), m_inputShapes[i].data(), m_inputShapes[i].size());
+            inputTensors.push_back(AZStd::move(inputTensor));
+        }
+
+        AZStd::vector<Ort::Value> outputTensors;
+        for (int i = 0; i < m_outputCount; i++)
+        {
+            Ort::Value outputTensor =
+                Ort::Value::CreateTensor<float>(m_memoryInfo, m_outputs[i].data(), m_outputs[i].size(), m_outputShapes[i].data(), m_outputShapes[i].size());
+            outputTensors.push_back(AZStd::move(outputTensor));
+        }
+
+        Ort::RunOptions runOptions;
+        runOptions.SetRunLogVerbosityLevel(ORT_LOGGING_LEVEL_VERBOSE); // Gives more useful logging info if m_session.Run() fails.
+        m_session.Run(runOptions, m_inputNames.data(), inputTensors.data(), m_inputCount, m_outputNames.data(), outputTensors.data(), m_outputCount);
+
+        float delta = 1000.f * m_timer.GetDeltaTimeInSeconds(); // Finish timing of inference and get time in milliseconds.
+        m_delta = delta;
+
+        ONNXRequestBus::Broadcast(&::ONNX::ONNXRequestBus::Events::AddTimingSample, m_modelName.c_str(), m_delta, m_modelColor);
+    }
+} // namespace ONNX

+ 23 - 0
Gems/ONNX/Code/Source/Clients/ONNXModule.cpp

@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <ONNXModuleInterface.h>
+#include "ONNXSystemComponent.h"
+
+namespace ONNX
+{
+    class ONNXModule
+        : public ONNXModuleInterface
+    {
+    public:
+        AZ_RTTI(ONNXModule, "{E006F52B-8EC8-4DFE-AB9D-C5EF7A1A8F32}", ONNXModuleInterface);
+        AZ_CLASS_ALLOCATOR(ONNXModule, AZ::SystemAllocator, 0);
+    };
+}// namespace ONNX
+
+AZ_DECLARE_MODULE_CLASS(Gem_ONNX, ONNX::ONNXModule)

+ 135 - 0
Gems/ONNX/Code/Source/Clients/ONNXSystemComponent.cpp

@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include "ONNXSystemComponent.h"
+#include <ONNX/Model.h>
+
+#include <AzCore/Serialization/EditContext.h>
+#include <AzCore/Serialization/EditContextConstants.inl>
+#include <AzCore/Serialization/SerializeContext.h>
+
+namespace ONNX
+{
+    void ONNXSystemComponent::AddTimingSample(const char* modelName, float inferenceTimeInMilliseconds, AZ::Color modelColor)
+    {
+        m_timingStats.PushHistogramValue(modelName, inferenceTimeInMilliseconds, modelColor);
+    }
+
+    void ONNXSystemComponent::OnImGuiUpdate()
+    {
+        if (!m_timingStats.m_show)
+        {
+            return;
+        }
+
+        if (ImGui::Begin("ONNX"))
+        {
+            m_timingStats.OnImGuiUpdate();
+        }
+    }
+
+    void ONNXSystemComponent::OnImGuiMainMenuUpdate()
+    {
+        if (ImGui::BeginMenu("ONNX"))
+        {
+            ImGui::MenuItem(m_timingStats.GetName(), "", &m_timingStats.m_show);
+            ImGui::EndMenu();
+        }
+    }
+
+    void ONNXSystemComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serialize->Class<ONNXSystemComponent, AZ::Component>()->Version(0);
+
+            if (AZ::EditContext* ec = serialize->GetEditContext())
+            {
+                ec->Class<ONNXSystemComponent>("ONNX", "Provides ONNX Runtime functionality in O3DE")
+                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
+                    ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System"))
+                    ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
+            }
+        }
+    }
+
+    void ONNXSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
+    {
+        provided.push_back(AZ_CRC_CE("ONNXService"));
+    }
+
+    void ONNXSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
+    {
+        incompatible.push_back(AZ_CRC_CE("ONNXService"));
+    }
+
+    void ONNXSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
+    {
+    }
+
+    void ONNXSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
+    {
+    }
+
+    ONNXSystemComponent::ONNXSystemComponent()
+    {
+        if (ONNXInterface::Get() == nullptr)
+        {
+            ONNXInterface::Register(this);
+        }
+
+        m_timingStats.SetName("ONNX Inference Timing Statistics");
+        m_timingStats.SetHistogramBinCount(200);
+
+        ImGui::ImGuiUpdateListenerBus::Handler::BusConnect();
+    }
+
+    ONNXSystemComponent::~ONNXSystemComponent()
+    {
+        ImGui::ImGuiUpdateListenerBus::Handler::BusDisconnect();
+
+        if (ONNXInterface::Get() == this)
+        {
+            ONNXInterface::Unregister(this);
+        }
+    }
+
+    Ort::Env* ONNXSystemComponent::GetEnv()
+    {
+        return m_env.get();
+    }
+
+    Ort::AllocatorWithDefaultOptions* ONNXSystemComponent::GetAllocator()
+    {
+        return m_allocator.get();
+    }
+
+    void OnnxLoggingFunction(void*, OrtLoggingLevel, const char* category, const char* logId, const char* codeLocation, const char* message)
+    {
+        AZ_Printf("ONNX", "%s %s %s %s\n", category, logId, codeLocation, message);
+    }
+
+    // The global environment and memory allocator are initialised with the system component, and are accessed via the EBus from within the
+    // model. m_precomputedTimingData and m_precomputedTimingDataCuda are structs holding the test inference statistics run before the
+    // editor starts up, and used by the ImGui dashboard.
+    void ONNXSystemComponent::Init()
+    {
+        m_env = AZStd::make_unique<Ort::Env>(ORT_LOGGING_LEVEL_VERBOSE, "test_log", OnnxLoggingFunction, nullptr);
+        m_allocator = AZStd::make_unique<Ort::AllocatorWithDefaultOptions>();
+    }
+
+    void ONNXSystemComponent::Activate()
+    {
+        ONNXRequestBus::Handler::BusConnect();
+    }
+
+    void ONNXSystemComponent::Deactivate()
+    {
+        AZ::TickBus::Handler::BusDisconnect();
+    }
+} // namespace ONNX

+ 65 - 0
Gems/ONNX/Code/Source/Clients/ONNXSystemComponent.h

@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/Component/Component.h>
+#include <AzCore/Component/TickBus.h>
+#include <AzCore/IO/FileIO.h>
+#include <AzCore/std/smart_ptr/unique_ptr.h>
+#include <ImGuiBus.h>
+#include <LYImGuiUtils/HistogramGroup.h>
+#include <ONNX/ONNXBus.h>
+#include <imgui/imgui.h>
+
+namespace ONNX
+{
+    class ONNXSystemComponent
+        : public AZ::Component
+        , protected ONNXRequestBus::Handler
+        , public ImGui::ImGuiUpdateListenerBus::Handler
+    {
+    public:
+        AZ_COMPONENT(ONNXSystemComponent, "{CB6735F4-D404-4EE9-A37A-439EDDCC655D}");
+
+        static void Reflect(AZ::ReflectContext* context);
+
+        static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
+        static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
+        static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
+        static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
+
+        void OnImGuiUpdate() override;
+        void OnImGuiMainMenuUpdate() override;
+
+        AZStd::unique_ptr<Ort::Env> m_env;
+        Ort::Env* GetEnv() override;
+
+        AZStd::unique_ptr<Ort::AllocatorWithDefaultOptions> m_allocator;
+        Ort::AllocatorWithDefaultOptions* GetAllocator() override;
+
+        ONNXSystemComponent();
+        ~ONNXSystemComponent();
+
+    protected:
+        ////////////////////////////////////////////////////////////////////////
+        // ONNXRequestBus interface implementation
+        ImGui::LYImGuiUtils::HistogramGroup m_timingStats;
+
+        void AddTimingSample(const char* modelName, float inferenceTimeInMilliseconds, AZ::Color modelColor) override;
+        ////////////////////////////////////////////////////////////////////////
+
+        ////////////////////////////////////////////////////////////////////////
+        // AZ::Component interface implementation
+        void Init() override;
+        void Activate() override;
+        void Deactivate() override;
+        ////////////////////////////////////////////////////////////////////////
+    };
+
+} // namespace ONNX

+ 43 - 0
Gems/ONNX/Code/Source/ONNXModuleInterface.h

@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AzCore/Memory/SystemAllocator.h>
+#include <AzCore/Module/Module.h>
+#include <Clients/ONNXSystemComponent.h>
+
+namespace ONNX
+{
+    class ONNXModuleInterface
+        : public AZ::Module
+    {
+    public:
+        AZ_RTTI(ONNXModuleInterface, "{D5A80703-FF4C-46FD-8EFF-C1D4781B66F2}", AZ::Module);
+        AZ_CLASS_ALLOCATOR(ONNXModuleInterface, AZ::SystemAllocator, 0);
+
+        ONNXModuleInterface()
+        {
+            // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here.
+            // Add ALL components descriptors associated with this gem to m_descriptors.
+            // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and EditContext.
+            // This happens through the [MyComponent]::Reflect() function.
+            m_descriptors.insert(m_descriptors.end(), {
+                ONNXSystemComponent::CreateDescriptor(),
+                });
+        }
+
+        /**
+         * Add required SystemComponents to the SystemEntity.
+         */
+        AZ::ComponentTypeList GetRequiredSystemComponents() const override
+        {
+            return AZ::ComponentTypeList{
+                azrtti_typeid<ONNXSystemComponent>(),
+            };
+        }
+    };
+}// namespace ONNX

+ 45 - 0
Gems/ONNX/Code/Source/Tools/ONNXEditorModule.cpp

@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <ONNXModuleInterface.h>
+#include "ONNXEditorSystemComponent.h"
+
+namespace ONNX
+{
+    class ONNXEditorModule
+        : public ONNXModuleInterface
+    {
+    public:
+        AZ_RTTI(ONNXEditorModule, "{E006F52B-8EC8-4DFE-AB9D-C5EF7A1A8F32}", ONNXModuleInterface);
+        AZ_CLASS_ALLOCATOR(ONNXEditorModule, AZ::SystemAllocator, 0);
+
+        ONNXEditorModule()
+        {
+            // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here.
+            // Add ALL components descriptors associated with this gem to m_descriptors.
+            // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and EditContext.
+            // This happens through the [MyComponent]::Reflect() function.
+            m_descriptors.insert(m_descriptors.end(), {
+                ONNXEditorSystemComponent::CreateDescriptor(),
+            });
+        }
+
+        /**
+         * Add required SystemComponents to the SystemEntity.
+         * Non-SystemComponents should not be added here
+         */
+        AZ::ComponentTypeList GetRequiredSystemComponents() const override
+        {
+            return AZ::ComponentTypeList {
+                azrtti_typeid<ONNXEditorSystemComponent>(),
+            };
+        }
+    };
+}// namespace ONNX
+
+AZ_DECLARE_MODULE_CLASS(Gem_ONNX, ONNX::ONNXEditorModule)

+ 62 - 0
Gems/ONNX/Code/Source/Tools/ONNXEditorSystemComponent.cpp

@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AzCore/Serialization/SerializeContext.h>
+#include <AzToolsFramework/API/ToolsApplicationAPI.h>
+#include "ONNXEditorSystemComponent.h"
+
+namespace ONNX
+{
+    void ONNXEditorSystemComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serializeContext->Class<ONNXEditorSystemComponent, ONNXSystemComponent>()
+                ->Version(0);
+        }
+    }
+
+    ONNXEditorSystemComponent::ONNXEditorSystemComponent() = default;
+
+    ONNXEditorSystemComponent::~ONNXEditorSystemComponent() = default;
+
+    void ONNXEditorSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
+    {
+        BaseSystemComponent::GetProvidedServices(provided);
+        provided.push_back(AZ_CRC_CE("ONNXEditorService"));
+    }
+
+    void ONNXEditorSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
+    {
+        BaseSystemComponent::GetIncompatibleServices(incompatible);
+        incompatible.push_back(AZ_CRC_CE("ONNXEditorService"));
+    }
+
+    void ONNXEditorSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
+    {
+        BaseSystemComponent::GetRequiredServices(required);
+    }
+
+    void ONNXEditorSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
+    {
+        BaseSystemComponent::GetDependentServices(dependent);
+    }
+
+    void ONNXEditorSystemComponent::Activate()
+    {
+        ONNXSystemComponent::Activate();
+        AzToolsFramework::EditorEvents::Bus::Handler::BusConnect();
+    }
+
+    void ONNXEditorSystemComponent::Deactivate()
+    {
+        AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect();
+        ONNXSystemComponent::Deactivate();
+    }
+
+} // namespace ONNX

+ 39 - 0
Gems/ONNX/Code/Source/Tools/ONNXEditorSystemComponent.h

@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <Clients/ONNXSystemComponent.h>
+#include <AzToolsFramework/API/ToolsApplicationAPI.h>
+
+namespace ONNX
+{
+    /// System component for ONNX editor
+    class ONNXEditorSystemComponent
+        : public ONNXSystemComponent
+        , public AzToolsFramework::EditorEvents::Bus::Handler
+    {
+        using BaseSystemComponent = ONNXSystemComponent;
+    public:
+        AZ_COMPONENT(ONNXEditorSystemComponent, "{761BD9F8-5707-4104-B182-CF3A5C0C412E}", BaseSystemComponent);
+        static void Reflect(AZ::ReflectContext* context);
+
+        ONNXEditorSystemComponent();
+        ~ONNXEditorSystemComponent();
+
+    private:
+        static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
+        static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
+        static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
+        static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
+
+        // AZ::Component
+        void Activate() override;
+        void Deactivate() override;
+    };
+} // namespace ONNX

+ 105 - 0
Gems/ONNX/Code/Tests/MnistTests.cpp

@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <Mnist.h>
+#include <ONNXSystemComponent.h>
+
+#include <AzCore/Component/ComponentApplicationBus.h>
+#include <AzCore/Serialization/SerializeContext.h>
+#include <AzCore/Settings/SettingsRegistryImpl.h>
+#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
+#include <AzCore/Slice/SliceAssetHandler.h>
+#include <AzFramework/IO/LocalFileIO.h>
+#include <AzTest/AzTest.h>
+#include <AzTest/GemTestEnvironment.h>
+#include <AzToolsFramework/Commands/PreemptiveUndoCache.h>
+#include <AzToolsFramework/Entity/EditorEntityContextComponent.h>
+#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
+#include <QApplication>
+#include <UnitTest/ToolsTestApplication.h>
+
+namespace Mnist
+{
+    class MnistTestEnvironment : public AZ::Test::GemTestEnvironment
+    {
+        void AddGemsAndComponents() override
+        {
+            AddComponentDescriptors({ ::ONNX::ONNXSystemComponent::CreateDescriptor() });
+
+            AddRequiredComponents({ ::ONNX::ONNXSystemComponent::TYPEINFO_Uuid() });
+        }
+
+        void PreCreateApplication() override
+        {
+            m_fileIO = AZStd::make_unique<AZ::IO::LocalFileIO>();
+            AZ::IO::FileIOBase::SetInstance(m_fileIO.get());
+        }
+
+        void PostDestroyApplication() override
+        {
+            AZ::IO::FileIOBase::SetInstance(nullptr);
+            m_fileIO.reset();
+        }
+
+    private:
+        AZStd::unique_ptr<AZ::IO::LocalFileIO> m_fileIO;
+    };
+
+    class MnistFixture : public ::testing::Test
+    {
+    protected:
+        void SetUp() override
+        {
+            AZ::SettingsRegistryImpl localRegistry;
+            localRegistry.Set(AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder, AZ::Test::GetEngineRootPath());
+
+            // Look up the path to the ONNX Gem Folder (don't assume it is in the Engine root)
+            // via searching through the gem paths that are registered in the o3de manifest files
+            // Adding the ONNX gem as an active gem allows the alias of @gemroot:ONNX@ to be
+            // set in the fileIO
+            AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_ManifestGemsPaths(localRegistry);
+            AZ::Test::AddActiveGem("ONNX", localRegistry, AZ::IO::FileIOBase::GetInstance());
+        }
+
+        void TearDown() override
+        {
+        }
+    };
+
+    // Tests the MNIST model against 1000 images of each digit from the dataset (10,000 total), and checks model accuracy is above 90%.
+    // If CUDA is enabled, the same tests will be run using CUDA for inferencing.
+    TEST_F(MnistFixture, ModelAccuracyGreaterThan90PercentWithCpu)
+    {
+        InferenceData cpuInferenceData = RunMnistSuite(1000, false);
+
+        float accuracy = (float)cpuInferenceData.m_numberOfCorrectInferences / (float)cpuInferenceData.m_totalNumberOfInferences;
+        EXPECT_GT(accuracy, 0.9f);
+    }
+
+#ifdef ENABLE_CUDA
+    TEST_F(MnistFixture, ModelAccuracyGreaterThan90PercentWithCuda)
+    {
+        InferenceData gpuInferenceData = RunMnistSuite(1000, true);
+
+        float accuracy = (float)gpuInferenceData.m_numberOfCorrectInferences / (float)gpuInferenceData.m_totalNumberOfInferences;
+
+        EXPECT_GT(accuracy, 0.9f);
+    }
+#endif
+} // namespace Mnist
+
+AZTEST_EXPORT int AZ_UNIT_TEST_HOOK_NAME(int argc, char** argv)
+{
+    ::testing::InitGoogleMock(&argc, argv);
+    QApplication app(argc, argv);
+    AZ::Test::ApplyGlobalParameters(&argc, argv);
+    AZ::Test::printUnusedParametersWarning(argc, argv);
+    AZ::Test::addTestEnvironments({ new Mnist::MnistTestEnvironment });
+    int result = RUN_ALL_TESTS();
+    return result;
+}

+ 12 - 0
Gems/ONNX/Code/onnx_api_files.cmake

@@ -0,0 +1,12 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+    Include/ONNX/ONNXBus.h
+    Include/ONNX/Model.h
+)

+ 10 - 0
Gems/ONNX/Code/onnx_editor_api_files.cmake

@@ -0,0 +1,10 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+)

+ 12 - 0
Gems/ONNX/Code/onnx_editor_private_files.cmake

@@ -0,0 +1,12 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+    Source/Tools/ONNXEditorSystemComponent.cpp
+    Source/Tools/ONNXEditorSystemComponent.h
+)

+ 11 - 0
Gems/ONNX/Code/onnx_editor_shared_files.cmake

@@ -0,0 +1,11 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+    Source/Tools/ONNXEditorModule.cpp
+)

+ 10 - 0
Gems/ONNX/Code/onnx_editor_tests_files.cmake

@@ -0,0 +1,10 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+)

+ 15 - 0
Gems/ONNX/Code/onnx_private_files.cmake

@@ -0,0 +1,15 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+    Include/ONNX/Model.h
+    Source/Clients/Model.cpp
+    Source/ONNXModuleInterface.h
+    Source/Clients/ONNXSystemComponent.cpp
+    Source/Clients/ONNXSystemComponent.h
+)

+ 16 - 0
Gems/ONNX/Code/onnx_shared_files.cmake

@@ -0,0 +1,16 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+    Source/Clients/ONNXModule.cpp
+    Include/ONNX/Model.h
+    Source/Clients/Model.cpp
+    Source/ONNXModuleInterface.h
+    Source/Clients/ONNXSystemComponent.cpp
+    Source/Clients/ONNXSystemComponent.h
+)

+ 15 - 0
Gems/ONNX/Code/onnx_tests_files.cmake

@@ -0,0 +1,15 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+    Tests/MnistTests.cpp
+    Source/Clients/Mnist.h
+    Source/Clients/Mnist.cpp
+    Source/Clients/upng/upng.cpp
+    Source/Clients/upng/upng.h
+)

+ 19 - 0
Gems/ONNX/External/FindOnnx.cmake

@@ -0,0 +1,19 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(TARGET_WITH_NAMESPACE "Onnx")
+if (TARGET ${TARGET_WITH_NAMESPACE})
+    return()
+endif()
+
+set(Onnx_INCLUDE_DIR ${CMAKE_CURRENT_LIST_DIR}/onnxruntime/include)
+set(Onnx_LIB ${CMAKE_CURRENT_LIST_DIR}/onnxruntime/lib/${ONNXRUNTIME_DEPENDENCY})
+
+add_library(${TARGET_WITH_NAMESPACE} STATIC IMPORTED GLOBAL)
+
+set(Onnx_FOUND TRUE)

+ 40 - 0
Gems/ONNX/README.md

@@ -0,0 +1,40 @@
+# ONNX
+
+This is an experimental gem implementing [ONNX Runtime](https://onnxruntime.ai/) in O3DE and demoing it using inference examples from the MNIST dataset with an Editor dashboard displaying inference statistics. Image decoding is done using a modified version of [uPNG](https://github.com/elanthis/upng).
+
+![Image of ONNX Dashboard](https://user-images.githubusercontent.com/108667365/182392987-e39a38af-169e-47a8-b14d-c18c431386ee.png)
+
+## Setup
+
+1. Download the zip of the GPU version of ONNX Runtime v1.9.0 (Windows or Linux) from the [ONNX Runtime GitHub](https://github.com/microsoft/onnxruntime/releases/tag/v1.9.0), extract, and put the contents inside the *External/onnxruntime* folder in the gem. The path should look like *External/onnxruntime/\<include and lib folders in here\>*.
+2. Put your *.onnx* file inside the *Assets* folder of the gem. By default the Model wrapper class looks for a file called *model.onnx*. To run the MNIST example you need an MNIST model, which you can get from the [ONNX GitHub](https://github.com/onnx/models/tree/main/vision/classification/mnist).
+3. Download Johnathan Orsolini's [MNIST.png dataset](https://www.kaggle.com/datasets/playlist/mnistzip) from Kaggle, unzip (takes a while, there are a lot of pictures), and place inside the *Assets* folder of the gem (i.e. the path should look like *Assets/mnist_png/\<testing and training folders in here\>*).
+4. Download the [uPNG source](https://github.com/elanthis/upng) from GitHub and copy into *Code/Source/Clients*. The source for uPNG has to be modified to work with the build, see [Modifying uPNG](#modifying-upng) below. You must have the following 2 files in these locations (the other uPNG files are unnecessary):
+    - *Code/Source/Clients/upng/upng.c*
+    - *Code/Source/Clients/upng/upng.h*
+5. GPU inferencing using CUDA is ENABLED by default. Please see [Requirements to inference using GPU](#requirements-to-inference-using-gpu) below to make sure you have the correct dependencies installed. If you do not have a CUDA enabled NVIDIA GPU, or would like to run the gem without the CUDA example, then you must make sure that the definition `PUBLIC ENABLE_CUDA=true` on ***line 51*** in *Code/CMakeLists.txt* is either commented out or removed. When you do this, the Model class will inference using CPU regardless of params passed in.
+6. Add the [ONNX](#onnx) gem to your project using the [Project Manager](https://docs.o3de.org/docs/user-guide/project-config/add-remove-gems/) or the [Command Line Interface (CLI)](https://docs.o3de.org/docs/user-guide/project-config/add-remove-gems/#using-the-command-line-interface-cli). See the documentation on  [Adding and Removing Gems in a Project](https://docs.o3de.org/docs/user-guide/project-config/add-remove-gems/).
+7. Compile your project and run.
+8. Once the editor starts, and you go to edit a level with an initialized ONNX Model, you should be able to press the **HOME** (or equivalent) button on your keyboard to see the inference dashboard containing runtime graphs for any initialised model run history.
+9. The MNIST example is located in the **ONNX.Tests** project, and demos the ONNX Model class by loading in the MNIST ONNX model file and running several thousand inferences against different digits in the MNIST testing dataset.
+
+## Modifying uPNG:
+
+1. Change the extension of *Code/Source/Clients/upng/upng.c* to *Code/Source/Clients/upng/upng.cpp*
+2. Go into *Code/Source/Clients/upng/upng.cpp*
+3. Modify ***lines 1172-1176*** as follows:
+```diff
+-	file = fopen(filename, "rb");
+-	if (file == NULL) {
++   errno_t err = fopen_s(&file, filename, "rb");
++	if (err != NULL) {
+		SET_ERROR(upng, UPNG_ENOTFOUND);
+		return upng;
+	}
+```
+
+## Requirements to inference using GPU:
+
+- [CUDA Toolkit](https://developer.nvidia.com/cuda-toolkit) v11.4 or greater.
+- [CUDNN library](https://developer.nvidia.com/cudnn) v8.2.4 or greater.
+- [zlib](https://zlib.net/) v1.2.3 or greater.

+ 18 - 0
Gems/ONNX/Registry/assetprocessor_settings.setreg

@@ -0,0 +1,18 @@
+{
+    "Amazon": {
+        "AssetProcessor": {
+            "Settings": {
+                "ScanFolder ONNX/Assets": {
+                    "watch": "@GEMROOT:ONNX@/Assets",
+                    "recursive": 1,
+                    "order": 101
+                },
+                "ScanFolder ONNX/Registry": {
+                    "watch": "@GEMROOT:ONNX@/Registry",
+                    "recursive": 1,
+                    "order": 102
+                }
+            }
+        }
+    }
+}

+ 20 - 0
Gems/ONNX/gem.json

@@ -0,0 +1,20 @@
+{
+    "gem_name": "ONNX",
+    "display_name": "ONNX",
+    "license": "Apache-2.0 Or MIT",
+    "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt",
+    "origin": "Open 3D Engine - o3de.org",
+    "type": "Code",
+    "summary": "This is an experimental gem implementing ONNX Runtime in O3DE and demoing it using inference examples from the MNIST dataset with an Editor dashboard displaying inference statistics.",
+    "canonical_tags": [
+        "Gem"
+    ],
+    "user_tags": [
+        "Simulation"
+    ],
+    "icon_path": "preview.png",
+    "requirements": "",
+    "dependencies": [
+        "ImGui"
+    ]
+}

+ 3 - 0
Gems/ONNX/preview.png

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:248e3ffe1fc9ffc02afb2ba8914e222a5a5d13ac45a48b98c95ee062e959a94c
+size 4475