mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-30 20:18:08 +00:00
Initial Objective-C API (#7366)
Initial implementation of an Objective-C API.
This commit is contained in:
parent
78e583d08c
commit
d21304ceb0
29 changed files with 1131 additions and 33 deletions
|
|
@ -65,6 +65,7 @@ option(onnxruntime_MSVC_STATIC_RUNTIME "Compile for the static CRT" OFF)
|
|||
option(onnxruntime_GCC_STATIC_CPP_RUNTIME "Compile for the static libstdc++" OFF)
|
||||
option(onnxruntime_BUILD_UNIT_TESTS "Build ONNXRuntime unit tests" ON)
|
||||
option(onnxruntime_BUILD_CSHARP "Build C# library" OFF)
|
||||
option(onnxruntime_BUILD_OBJC "Build Objective-C library" OFF)
|
||||
option(onnxruntime_USE_PREINSTALLED_EIGEN "Use pre-installed EIGEN. Need to provide eigen_SOURCE_PATH if turn this on." OFF)
|
||||
option(onnxruntime_BUILD_BENCHMARKS "Build ONNXRuntime micro-benchmarks" OFF)
|
||||
|
||||
|
|
@ -1574,6 +1575,11 @@ if (onnxruntime_ENABLE_PYTHON)
|
|||
include(onnxruntime_python.cmake)
|
||||
endif()
|
||||
|
||||
if (onnxruntime_BUILD_OBJC)
|
||||
message(STATUS "Objective-C Build is enabled")
|
||||
include(onnxruntime_objectivec.cmake)
|
||||
endif()
|
||||
|
||||
if (onnxruntime_BUILD_UNIT_TESTS)
|
||||
include(onnxruntime_unittests.cmake)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -79,7 +79,6 @@ endif()
|
|||
|
||||
add_dependencies(onnxruntime onnxruntime_generate_def ${onnxruntime_EXTERNAL_DEPENDENCIES})
|
||||
target_include_directories(onnxruntime PRIVATE ${ONNXRUNTIME_ROOT})
|
||||
onnxruntime_add_include_to_target(onnxruntime)
|
||||
|
||||
target_compile_definitions(onnxruntime PRIVATE VER_MAJOR=${VERSION_MAJOR_PART})
|
||||
target_compile_definitions(onnxruntime PRIVATE VER_MINOR=${VERSION_MINOR_PART})
|
||||
|
|
|
|||
|
|
@ -119,10 +119,9 @@ target_include_directories(onnxruntime_common
|
|||
PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${ONNXRUNTIME_ROOT} ${eigen_INCLUDE_DIRS}
|
||||
# propagate include directories of dependencies that are part of public interface
|
||||
PUBLIC
|
||||
$<TARGET_PROPERTY:safeint_interface,INTERFACE_INCLUDE_DIRECTORIES>
|
||||
${OPTIONAL_LITE_INCLUDE_DIR})
|
||||
|
||||
target_link_libraries(onnxruntime_common Boost::mp11)
|
||||
target_link_libraries(onnxruntime_common safeint_interface Boost::mp11)
|
||||
|
||||
if(NOT WIN32)
|
||||
target_include_directories(onnxruntime_common PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/external/nsync/public")
|
||||
|
|
|
|||
124
cmake/onnxruntime_objectivec.cmake
Normal file
124
cmake/onnxruntime_objectivec.cmake
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_LESS "3.18")
|
||||
message(FATAL_ERROR "CMake 3.18+ is required when building the Objective-C API.")
|
||||
endif()
|
||||
|
||||
check_language(OBJC)
|
||||
if(CMAKE_OBJC_COMPILER)
|
||||
enable_language(OBJC)
|
||||
else()
|
||||
message(FATAL_ERROR "Objective-C is not supported.")
|
||||
endif()
|
||||
|
||||
check_language(OBJCXX)
|
||||
if(CMAKE_OBJCXX_COMPILER)
|
||||
enable_language(OBJCXX)
|
||||
else()
|
||||
message(FATAL_ERROR "Objective-C++ is not supported.")
|
||||
endif()
|
||||
|
||||
add_compile_options(
|
||||
"$<$<COMPILE_LANGUAGE:OBJC,OBJCXX>:-Wall>"
|
||||
"$<$<COMPILE_LANGUAGE:OBJC,OBJCXX>:-Wextra>")
|
||||
if(onnxruntime_DEV_MODE)
|
||||
add_compile_options(
|
||||
"$<$<COMPILE_LANGUAGE:OBJC,OBJCXX>:-Werror>")
|
||||
endif()
|
||||
|
||||
set(OBJC_ROOT "${REPO_ROOT}/objectivec")
|
||||
|
||||
set(OBJC_ARC_COMPILE_OPTIONS "-fobjc-arc" "-fobjc-arc-exceptions")
|
||||
|
||||
# onnxruntime_objc target
|
||||
|
||||
# these headers are the public interface
|
||||
# explicitly list them here so it is easy to see what is included
|
||||
set(onnxruntime_objc_headers
|
||||
"${OBJC_ROOT}/include/onnxruntime.h"
|
||||
"${OBJC_ROOT}/include/onnxruntime/ort_env.h"
|
||||
"${OBJC_ROOT}/include/onnxruntime/ort_session.h"
|
||||
"${OBJC_ROOT}/include/onnxruntime/ort_value.h")
|
||||
|
||||
file(GLOB onnxruntime_objc_srcs
|
||||
"${OBJC_ROOT}/src/*.h"
|
||||
"${OBJC_ROOT}/src/*.m"
|
||||
"${OBJC_ROOT}/src/*.mm")
|
||||
|
||||
# files common to implementation and test targets
|
||||
set(onnxruntime_objc_common_srcs
|
||||
"${OBJC_ROOT}/common/assert_arc_enabled.mm")
|
||||
|
||||
source_group(TREE "${OBJC_ROOT}" FILES
|
||||
${onnxruntime_objc_headers}
|
||||
${onnxruntime_objc_srcs}
|
||||
${onnxruntime_objc_common_srcs})
|
||||
|
||||
add_library(onnxruntime_objc SHARED
|
||||
${onnxruntime_objc_headers}
|
||||
${onnxruntime_objc_srcs}
|
||||
${onnxruntime_objc_common_srcs})
|
||||
|
||||
target_include_directories(onnxruntime_objc
|
||||
PUBLIC
|
||||
"${OBJC_ROOT}/include"
|
||||
PRIVATE
|
||||
"${ONNXRUNTIME_ROOT}"
|
||||
"${OBJC_ROOT}")
|
||||
|
||||
find_library(FOUNDATION_LIB Foundation REQUIRED)
|
||||
|
||||
target_link_libraries(onnxruntime_objc
|
||||
PRIVATE
|
||||
onnxruntime
|
||||
safeint_interface
|
||||
${FOUNDATION_LIB})
|
||||
|
||||
target_compile_options(onnxruntime_objc PRIVATE ${OBJC_ARC_COMPILE_OPTIONS})
|
||||
|
||||
set_target_properties(onnxruntime_objc PROPERTIES
|
||||
FRAMEWORK TRUE
|
||||
VERSION "1.0.0"
|
||||
SOVERSION "1.0.0"
|
||||
FRAMEWORK_VERSION "A"
|
||||
PUBLIC_HEADER "${onnxruntime_objc_headers}"
|
||||
FOLDER "ONNXRuntime"
|
||||
CXX_STANDARD 17) # TODO remove when everything else moves to 17
|
||||
|
||||
if(onnxruntime_BUILD_UNIT_TESTS)
|
||||
find_package(XCTest REQUIRED)
|
||||
|
||||
# onnxruntime_test_objc target
|
||||
|
||||
file(GLOB onnxruntime_objc_test_srcs
|
||||
"${OBJC_ROOT}/test/*.h"
|
||||
"${OBJC_ROOT}/test/*.m"
|
||||
"${OBJC_ROOT}/test/*.mm")
|
||||
|
||||
source_group(TREE "${OBJC_ROOT}" FILES ${onnxruntime_objc_test_srcs})
|
||||
|
||||
xctest_add_bundle(onnxruntime_objc_test onnxruntime_objc
|
||||
${onnxruntime_objc_headers}
|
||||
${onnxruntime_objc_test_srcs}
|
||||
${onnxruntime_objc_common_srcs})
|
||||
|
||||
target_include_directories(onnxruntime_objc_test
|
||||
PRIVATE
|
||||
"${OBJC_ROOT}")
|
||||
|
||||
target_compile_options(onnxruntime_objc_test PRIVATE ${OBJC_ARC_COMPILE_OPTIONS})
|
||||
|
||||
set_target_properties(onnxruntime_objc_test PROPERTIES
|
||||
FOLDER "ONNXRuntimeTest")
|
||||
|
||||
add_custom_command(TARGET onnxruntime_objc_test POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
"${OBJC_ROOT}/test/testdata"
|
||||
"$<TARGET_BUNDLE_CONTENT_DIR:onnxruntime_objc_test>/Resources/testdata")
|
||||
|
||||
xctest_add_test(XCTest.onnxruntime_objc_test onnxruntime_objc_test)
|
||||
|
||||
set_property(TEST XCTest.onnxruntime_objc_test APPEND PROPERTY
|
||||
ENVIRONMENT "DYLD_LIBRARY_PATH=$<TARGET_FILE_DIR:onnxruntime>")
|
||||
endif()
|
||||
|
|
@ -1041,7 +1041,7 @@ if (onnxruntime_USE_ROCM)
|
|||
endif()
|
||||
# During transition to separate hipFFT repo, put hipfft/include early
|
||||
target_include_directories(onnxruntime_providers_rocm PRIVATE ${onnxruntime_ROCM_HOME}/hipfft/include ${onnxruntime_ROCM_HOME}/include ${onnxruntime_ROCM_HOME}/hipcub/include ${onnxruntime_ROCM_HOME}/hiprand/include ${onnxruntime_ROCM_HOME}/rocrand/include)
|
||||
target_include_directories(onnxruntime_providers_rocm PRIVATE ${ONNXRUNTIME_ROOT} ${CMAKE_CURRENT_BINARY_DIR}/amdgpu/onnxruntime ${MPI_INCLUDE_DIRS} ${SAFEINT_INCLUDE_DIR} ${ONNXRUNTIME_ROOT}/../cmake/external/eigen)
|
||||
target_include_directories(onnxruntime_providers_rocm PRIVATE ${ONNXRUNTIME_ROOT} ${CMAKE_CURRENT_BINARY_DIR}/amdgpu/onnxruntime ${MPI_INCLUDE_DIRS} ${ONNXRUNTIME_ROOT}/../cmake/external/eigen)
|
||||
|
||||
if (onnxruntime_ENABLE_TRAINING)
|
||||
target_include_directories(onnxruntime_providers_rocm PRIVATE ${ORTTRAINING_ROOT} ${CMAKE_CURRENT_BINARY_DIR}/amdgpu/orttraining)
|
||||
|
|
|
|||
|
|
@ -70,3 +70,8 @@ The [autopep8](https://pypi.org/project/autopep8/) tool can be used to automatic
|
|||
|
||||
Editors such as PyCharm [(see here)](https://www.jetbrains.com/help/pycharm/code-inspection.html) and Visual Studio Code [(see here)](https://code.visualstudio.com/docs/python/linting#_flake8) can be configured to check for PEP8 issues.
|
||||
|
||||
## Objective-C/C++ Code Style
|
||||
|
||||
Please follow the [Google Objective-C/C++ Style Guide](https://google.github.io/styleguide/objcguide.html).
|
||||
|
||||
Clang-format can be used to format Objective-C/C++ code. The .clang-format file is in the repository root directory.
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ ORT_RUNTIME_CLASS(Env);
|
|||
ORT_RUNTIME_CLASS(Status); // nullptr for Status* indicates success
|
||||
ORT_RUNTIME_CLASS(MemoryInfo);
|
||||
ORT_RUNTIME_CLASS(IoBinding);
|
||||
ORT_RUNTIME_CLASS(Session); //Don't call OrtReleaseSession from Dllmain (because session owns a thread pool)
|
||||
ORT_RUNTIME_CLASS(Session); //Don't call ReleaseSession from Dllmain (because session owns a thread pool)
|
||||
ORT_RUNTIME_CLASS(Value);
|
||||
ORT_RUNTIME_CLASS(RunOptions);
|
||||
ORT_RUNTIME_CLASS(TypeInfo);
|
||||
|
|
@ -343,12 +343,12 @@ struct OrtApi {
|
|||
const char*(ORT_API_CALL* GetErrorMessage)(_In_ const OrtStatus* status)NO_EXCEPTION ORT_ALL_ARGS_NONNULL;
|
||||
|
||||
/**
|
||||
* \param out Should be freed by `OrtReleaseEnv` after use
|
||||
* \param out Should be freed by `ReleaseEnv` after use
|
||||
*/
|
||||
ORT_API2_STATUS(CreateEnv, OrtLoggingLevel logging_level, _In_ const char* logid, _Outptr_ OrtEnv** out);
|
||||
|
||||
/**
|
||||
* \param out Should be freed by `OrtReleaseEnv` after use
|
||||
* \param out Should be freed by `ReleaseEnv` after use
|
||||
*/
|
||||
ORT_API2_STATUS(CreateEnvWithCustomLogger, OrtLoggingFunction logging_function, _In_opt_ void* logger_param,
|
||||
OrtLoggingLevel logging_level, _In_ const char* logid, _Outptr_ OrtEnv** out);
|
||||
|
|
@ -375,7 +375,7 @@ struct OrtApi {
|
|||
_Inout_updates_all_(output_names_len) OrtValue** output);
|
||||
|
||||
/**
|
||||
* \return A pointer of the newly created object. The pointer should be freed by OrtReleaseSessionOptions after use
|
||||
* \return A pointer of the newly created object. The pointer should be freed by ReleaseSessionOptions after use
|
||||
*/
|
||||
ORT_API2_STATUS(CreateSessionOptions, _Outptr_ OrtSessionOptions** options);
|
||||
|
||||
|
|
@ -432,7 +432,7 @@ struct OrtApi {
|
|||
ORT_API2_STATUS(SetInterOpNumThreads, _Inout_ OrtSessionOptions* options, int inter_op_num_threads);
|
||||
|
||||
/*
|
||||
Create a custom op domain. After all sessions using it are released, call OrtReleaseCustomOpDomain
|
||||
Create a custom op domain. After all sessions using it are released, call ReleaseCustomOpDomain
|
||||
*/
|
||||
ORT_API2_STATUS(CreateCustomOpDomain, _In_ const char* domain, _Outptr_ OrtCustomOpDomain** out);
|
||||
|
||||
|
|
@ -474,18 +474,18 @@ struct OrtApi {
|
|||
ORT_API2_STATUS(SessionGetOverridableInitializerCount, _In_ const OrtSession* sess, _Out_ size_t* out);
|
||||
|
||||
/**
|
||||
* \param out should be freed by OrtReleaseTypeInfo after use
|
||||
* \param out should be freed by ReleaseTypeInfo after use
|
||||
*/
|
||||
ORT_API2_STATUS(SessionGetInputTypeInfo, _In_ const OrtSession* sess, size_t index, _Outptr_ OrtTypeInfo** type_info);
|
||||
|
||||
/**
|
||||
* \param out should be freed by OrtReleaseTypeInfo after use
|
||||
* \param out should be freed by ReleaseTypeInfo after use
|
||||
*/
|
||||
ORT_API2_STATUS(SessionGetOutputTypeInfo, _In_ const OrtSession* sess, size_t index,
|
||||
_Outptr_ OrtTypeInfo** type_info);
|
||||
|
||||
/**
|
||||
* \param out should be freed by OrtReleaseTypeInfo after use
|
||||
* \param out should be freed by ReleaseTypeInfo after use
|
||||
*/
|
||||
ORT_API2_STATUS(SessionGetOverridableInitializerTypeInfo, _In_ const OrtSession* sess, size_t index,
|
||||
_Outptr_ OrtTypeInfo** type_info);
|
||||
|
|
@ -501,7 +501,7 @@ struct OrtApi {
|
|||
_Inout_ OrtAllocator* allocator, _Outptr_ char** value);
|
||||
|
||||
/**
|
||||
* \return A pointer to the newly created object. The pointer should be freed by OrtReleaseRunOptions after use
|
||||
* \return A pointer to the newly created object. The pointer should be freed by ReleaseRunOptions after use
|
||||
*/
|
||||
ORT_API2_STATUS(CreateRunOptions, _Outptr_ OrtRunOptions** out);
|
||||
|
||||
|
|
@ -520,8 +520,8 @@ struct OrtApi {
|
|||
ORT_API2_STATUS(RunOptionsUnsetTerminate, _Inout_ OrtRunOptions* options);
|
||||
|
||||
/**
|
||||
* Create a tensor from an allocator. OrtReleaseValue will also release the buffer inside the output value
|
||||
* \param out Should be freed by calling OrtReleaseValue
|
||||
* Create a tensor from an allocator. ReleaseValue will also release the buffer inside the output value
|
||||
* \param out Should be freed by calling ReleaseValue
|
||||
* \param type must be one of TENSOR_ELEMENT_DATA_TYPE_xxxx
|
||||
*/
|
||||
ORT_API2_STATUS(CreateTensorAsOrtValue, _Inout_ OrtAllocator* allocator, _In_ const int64_t* shape, size_t shape_len,
|
||||
|
|
@ -529,8 +529,8 @@ struct OrtApi {
|
|||
|
||||
/**
|
||||
* Create a tensor with user's buffer. You can fill the buffer either before calling this function or after.
|
||||
* p_data is owned by caller. OrtReleaseValue won't release p_data.
|
||||
* \param out Should be freed by calling OrtReleaseValue
|
||||
* p_data is owned by caller. ReleaseValue won't release p_data.
|
||||
* \param out Should be freed by calling ReleaseValue
|
||||
*/
|
||||
ORT_API2_STATUS(CreateTensorWithDataAsOrtValue, _In_ const OrtMemoryInfo* info, _Inout_ void* p_data,
|
||||
size_t p_data_len, _In_ const int64_t* shape, size_t shape_len, ONNXTensorElementDataType type,
|
||||
|
|
@ -578,7 +578,7 @@ struct OrtApi {
|
|||
ORT_API2_STATUS(GetOnnxTypeFromTypeInfo, _In_ const OrtTypeInfo*, _Out_ enum ONNXType* out);
|
||||
|
||||
/**
|
||||
* The 'out' value should be released by calling OrtReleaseTensorTypeAndShapeInfo
|
||||
* The 'out' value should be released by calling ReleaseTensorTypeAndShapeInfo
|
||||
*/
|
||||
ORT_API2_STATUS(CreateTensorTypeAndShapeInfo, _Outptr_ OrtTensorTypeAndShapeInfo** out);
|
||||
|
||||
|
|
@ -611,14 +611,14 @@ struct OrtApi {
|
|||
ORT_API2_STATUS(GetTensorShapeElementCount, _In_ const OrtTensorTypeAndShapeInfo* info, _Out_ size_t* out);
|
||||
|
||||
/**
|
||||
* \param out Should be freed by OrtReleaseTensorTypeAndShapeInfo after use
|
||||
* \param out Should be freed by ReleaseTensorTypeAndShapeInfo after use
|
||||
*/
|
||||
ORT_API2_STATUS(GetTensorTypeAndShape, _In_ const OrtValue* value, _Outptr_ OrtTensorTypeAndShapeInfo** out);
|
||||
|
||||
/**
|
||||
* Get the type information of an OrtValue
|
||||
* \param value
|
||||
* \param out The returned value should be freed by OrtReleaseTypeInfo after use
|
||||
* \param out The returned value should be freed by ReleaseTypeInfo after use
|
||||
*/
|
||||
ORT_API2_STATUS(GetTypeInfo, _In_ const OrtValue* value, _Outptr_result_maybenull_ OrtTypeInfo** out);
|
||||
|
||||
|
|
@ -793,7 +793,7 @@ struct OrtApi {
|
|||
ORT_CLASS_RELEASE(Env);
|
||||
ORT_CLASS_RELEASE(Status); // nullptr for Status* indicates success
|
||||
ORT_CLASS_RELEASE(MemoryInfo);
|
||||
ORT_CLASS_RELEASE(Session); //Don't call OrtReleaseSession from Dllmain (because session owns a thread pool)
|
||||
ORT_CLASS_RELEASE(Session); //Don't call ReleaseSession from Dllmain (because session owns a thread pool)
|
||||
ORT_CLASS_RELEASE(Value);
|
||||
ORT_CLASS_RELEASE(RunOptions);
|
||||
ORT_CLASS_RELEASE(TypeInfo);
|
||||
|
|
@ -1156,7 +1156,7 @@ struct OrtApi {
|
|||
* Use this in conjunction with DisablePerSessionThreads API or else the session will use
|
||||
* its own thread pools.
|
||||
*
|
||||
* \param out should be freed by `OrtReleaseEnv` after use
|
||||
* \param out should be freed by `ReleaseEnv` after use
|
||||
*/
|
||||
ORT_API2_STATUS(CreateEnvWithCustomLoggerAndGlobalThreadPools, OrtLoggingFunction logging_function, _In_opt_ void* logger_param, OrtLoggingLevel logging_level,
|
||||
_In_ const char* logid, _In_ const struct OrtThreadingOptions* tp_options, _Outptr_ OrtEnv** out);
|
||||
|
|
|
|||
4
objectivec/common/assert_arc_enabled.mm
Normal file
4
objectivec/common/assert_arc_enabled.mm
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
static_assert(__has_feature(objc_arc), "Objective-C ARC must be enabled.");
|
||||
10
objectivec/format_objc.sh
Executable file
10
objectivec/format_objc.sh
Executable file
|
|
@ -0,0 +1,10 @@
|
|||
#!/bin/bash
|
||||
|
||||
# formats Objective-C/C++ code
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
clang-format -i $(find ${SCRIPT_DIR} -name "*.h" -o -name "*.m" -o -name "*.mm")
|
||||
|
||||
9
objectivec/include/onnxruntime.h
Normal file
9
objectivec/include/onnxruntime.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
// this header contains the entire ONNX Runtime Objective-C API
|
||||
// the headers below can also be imported individually
|
||||
|
||||
#import "onnxruntime/ort_env.h"
|
||||
#import "onnxruntime/ort_session.h"
|
||||
#import "onnxruntime/ort_value.h"
|
||||
25
objectivec/include/onnxruntime/ort_env.h
Normal file
25
objectivec/include/onnxruntime/ort_env.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The ORT environment.
|
||||
*/
|
||||
@interface ORTEnv : NSObject
|
||||
|
||||
- (nullable instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
* Creates an ORT Environment.
|
||||
*
|
||||
* @param[out] error Optional error information set if an error occurs.
|
||||
* @return The instance, or nil if an error occurs.
|
||||
*/
|
||||
- (nullable instancetype)initWithError:(NSError**)error NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
59
objectivec/include/onnxruntime/ort_session.h
Normal file
59
objectivec/include/onnxruntime/ort_session.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ORTEnv;
|
||||
@class ORTValue;
|
||||
|
||||
/**
|
||||
* An ORT session loads and runs a model.
|
||||
*/
|
||||
@interface ORTSession : NSObject
|
||||
|
||||
- (nullable instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
* Creates an ORT Session.
|
||||
*
|
||||
* @param env The ORT Environment instance.
|
||||
* @param path The path to the ONNX model.
|
||||
* @param[out] error Optional error information set if an error occurs.
|
||||
* @return The instance, or nil if an error occurs.
|
||||
*/
|
||||
- (nullable instancetype)initWithEnv:(ORTEnv*)env
|
||||
modelPath:(NSString*)path
|
||||
error:(NSError**)error NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* Runs the model.
|
||||
* The inputs and outputs are pre-allocated.
|
||||
*
|
||||
* @param inputs Dictionary of input names to input ORT values.
|
||||
* @param outputs Dictionary of output names to output ORT values.
|
||||
* @param[out] error Optional error information set if an error occurs.
|
||||
* @return Whether the model was run successfully.
|
||||
*/
|
||||
- (BOOL)runWithInputs:(NSDictionary<NSString*, ORTValue*>*)inputs
|
||||
outputs:(NSDictionary<NSString*, ORTValue*>*)outputs
|
||||
error:(NSError**)error;
|
||||
|
||||
/**
|
||||
* Runs the model.
|
||||
* The inputs are pre-allocated and the outputs are allocated by ORT.
|
||||
*
|
||||
* @param inputs Dictionary of input names to input ORT values.
|
||||
* @param outputNames Set of output names.
|
||||
* @param[out] error Optional error information set if an error occurs.
|
||||
* @return A dictionary of output names to output ORT values with the outputs
|
||||
* requested in `outputNames`, or nil if an error occurs.
|
||||
*/
|
||||
- (nullable NSDictionary<NSString*, ORTValue*>*)runWithInputs:(NSDictionary<NSString*, ORTValue*>*)inputs
|
||||
outputNames:(NSSet<NSString*>*)outputNames
|
||||
error:(NSError**)error;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
88
objectivec/include/onnxruntime/ort_value.h
Normal file
88
objectivec/include/onnxruntime/ort_value.h
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The supported ORT value types.
|
||||
*/
|
||||
typedef NS_ENUM(int32_t, ORTValueType) {
|
||||
ORTValueTypeUnknown,
|
||||
ORTValueTypeTensor,
|
||||
};
|
||||
|
||||
/**
|
||||
* The supported ORT tensor element data types.
|
||||
*/
|
||||
typedef NS_ENUM(int32_t, ORTTensorElementDataType) {
|
||||
ORTTensorElementDataTypeUndefined,
|
||||
ORTTensorElementDataTypeFloat,
|
||||
ORTTensorElementDataTypeInt32,
|
||||
};
|
||||
|
||||
/**
|
||||
* An ORT value encapsulates data used as an input or output to a model at runtime.
|
||||
*/
|
||||
@interface ORTValue : NSObject
|
||||
|
||||
- (nullable instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
* Creates a value that is a tensor.
|
||||
* The tensor data is allocated by the caller.
|
||||
*
|
||||
* @param data The tensor data.
|
||||
* @param type The tensor data element type.
|
||||
* @param shape The tensor shape.
|
||||
* @param[out] error Optional error information set if an error occurs.
|
||||
* @return The instance, or nil if an error occurs.
|
||||
*/
|
||||
- (nullable instancetype)initTensorWithData:(NSMutableData*)tensorData
|
||||
elementType:(ORTTensorElementDataType)elementType
|
||||
shape:(NSArray<NSNumber*>*)shape
|
||||
error:(NSError**)error;
|
||||
|
||||
/**
|
||||
* Gets the value type.
|
||||
*
|
||||
* @param[out] valueType The type of the value.
|
||||
* @param[out] error Optional error information set if an error occurs.
|
||||
* @return Whether the value type was retrieved successfully.
|
||||
*/
|
||||
- (BOOL)valueType:(ORTValueType*)valueType
|
||||
error:(NSError**)error;
|
||||
|
||||
/**
|
||||
* Gets the tensor data element type.
|
||||
* This assumes that the value is a tensor.
|
||||
*
|
||||
* @param[out] elementType The type of the tensor's data elements.
|
||||
* @param[out] error Optional error information set if an error occurs.
|
||||
* @return Whether the tensor data element type was retrieved successfully.
|
||||
*/
|
||||
- (BOOL)tensorElementType:(ORTTensorElementDataType*)elementType
|
||||
error:(NSError**)error;
|
||||
|
||||
/**
|
||||
* Gets the tensor shape.
|
||||
* This assumes that the value is a tensor.
|
||||
*
|
||||
* @param[out] error Optional error information set if an error occurs.
|
||||
* @return The tensor shape, or nil if an error occurs.
|
||||
*/
|
||||
- (nullable NSArray<NSNumber*>*)tensorShapeWithError:(NSError**)error;
|
||||
|
||||
/**
|
||||
* Gets the tensor data.
|
||||
* This assumes that the value is a tensor.
|
||||
*
|
||||
* @param[out] error Optional error information set if an error occurs.
|
||||
* @return The tensor data, or nil if an error occurs.
|
||||
*/
|
||||
- (nullable NSMutableData*)tensorDataWithError:(NSError**)error;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
13
objectivec/src/error_utils.h
Normal file
13
objectivec/src/error_utils.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include "core/session/onnxruntime_cxx_api.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
void ORTSaveCodeAndDescriptionToError(int code, const char* description, NSError** error);
|
||||
void ORTSaveExceptionToError(const Ort::Exception& e, NSError** error);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
25
objectivec/src/error_utils.mm
Normal file
25
objectivec/src/error_utils.mm
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#import "src/error_utils.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
static NSString* const kOrtErrorDomain = @"onnxruntime";
|
||||
|
||||
void ORTSaveCodeAndDescriptionToError(int code, const char* descriptionCstr, NSError** error) {
|
||||
if (!error) return;
|
||||
|
||||
NSString* description = [NSString stringWithCString:descriptionCstr
|
||||
encoding:NSASCIIStringEncoding];
|
||||
|
||||
*error = [NSError errorWithDomain:kOrtErrorDomain
|
||||
code:code
|
||||
userInfo:@{NSLocalizedDescriptionKey : description}];
|
||||
}
|
||||
|
||||
void ORTSaveExceptionToError(const Ort::Exception& e, NSError** error) {
|
||||
ORTSaveCodeAndDescriptionToError(e.GetOrtErrorCode(), e.what(), error);
|
||||
}
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
37
objectivec/src/ort_env.mm
Normal file
37
objectivec/src/ort_env.mm
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#import "src/ort_env_internal.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "core/session/onnxruntime_cxx_api.h"
|
||||
|
||||
#import "src/error_utils.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation ORTEnv {
|
||||
std::optional<Ort::Env> _env;
|
||||
}
|
||||
|
||||
- (nullable instancetype)initWithError:(NSError**)error {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
try {
|
||||
_env = Ort::Env{};
|
||||
} catch (const Ort::Exception& e) {
|
||||
ORTSaveExceptionToError(e, error);
|
||||
self = nil;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (Ort::Env&)CXXAPIOrtEnv {
|
||||
return *_env;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
16
objectivec/src/ort_env_internal.h
Normal file
16
objectivec/src/ort_env_internal.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#import "onnxruntime/ort_env.h"
|
||||
|
||||
#include "core/session/onnxruntime_cxx_api.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ORTEnv ()
|
||||
|
||||
- (Ort::Env&)CXXAPIOrtEnv;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
118
objectivec/src/ort_session.mm
Normal file
118
objectivec/src/ort_session.mm
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#import "onnxruntime/ort_session.h"
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "core/session/onnxruntime_cxx_api.h"
|
||||
|
||||
#import "src/error_utils.h"
|
||||
#import "src/ort_env_internal.h"
|
||||
#import "src/ort_value_internal.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation ORTSession {
|
||||
std::optional<Ort::Session> _session;
|
||||
}
|
||||
|
||||
- (nullable instancetype)initWithEnv:(ORTEnv*)env
|
||||
modelPath:(NSString*)path
|
||||
error:(NSError**)error {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
try {
|
||||
Ort::SessionOptions sessionOptions{}; // TODO make configurable
|
||||
_session = Ort::Session{[env CXXAPIOrtEnv], path.UTF8String, sessionOptions};
|
||||
} catch (const Ort::Exception& e) {
|
||||
ORTSaveExceptionToError(e, error);
|
||||
self = nil;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)runWithInputs:(NSDictionary<NSString*, ORTValue*>*)inputs
|
||||
outputs:(NSDictionary<NSString*, ORTValue*>*)outputs
|
||||
error:(NSError**)error {
|
||||
BOOL status = NO;
|
||||
try {
|
||||
Ort::RunOptions runOptions{}; // TODO make configurable
|
||||
|
||||
std::vector<const char*> inputNames, outputNames;
|
||||
std::vector<const OrtValue*> inputValues;
|
||||
std::vector<OrtValue*> outputValues;
|
||||
|
||||
for (NSString* inputName in inputs) {
|
||||
inputNames.push_back(inputName.UTF8String);
|
||||
inputValues.push_back(static_cast<const OrtValue*>([inputs[inputName] CXXAPIOrtValue]));
|
||||
}
|
||||
|
||||
for (NSString* outputName in outputs) {
|
||||
outputNames.push_back(outputName.UTF8String);
|
||||
outputValues.push_back(static_cast<OrtValue*>([outputs[outputName] CXXAPIOrtValue]));
|
||||
}
|
||||
|
||||
Ort::ThrowOnError(Ort::GetApi().Run(*_session, runOptions,
|
||||
inputNames.data(), inputValues.data(), inputNames.size(),
|
||||
outputNames.data(), outputNames.size(), outputValues.data()));
|
||||
|
||||
status = YES;
|
||||
} catch (const Ort::Exception& e) {
|
||||
ORTSaveExceptionToError(e, error);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
- (nullable NSDictionary<NSString*, ORTValue*>*)runWithInputs:(NSDictionary<NSString*, ORTValue*>*)inputs
|
||||
outputNames:(NSSet<NSString*>*)outputNameSet
|
||||
error:(NSError**)error {
|
||||
try {
|
||||
NSArray<NSString*>* outputNameArray = outputNameSet.allObjects;
|
||||
|
||||
Ort::RunOptions runOptions{}; // TODO make configurable
|
||||
|
||||
std::vector<const char*> inputNames, outputNames;
|
||||
std::vector<const OrtValue*> inputValues;
|
||||
std::vector<OrtValue*> outputValues;
|
||||
|
||||
for (NSString* inputName in inputs) {
|
||||
inputNames.push_back(inputName.UTF8String);
|
||||
inputValues.push_back(static_cast<const OrtValue*>([inputs[inputName] CXXAPIOrtValue]));
|
||||
}
|
||||
|
||||
for (NSString* outputName in outputNameArray) {
|
||||
outputNames.push_back(outputName.UTF8String);
|
||||
outputValues.push_back(nullptr);
|
||||
}
|
||||
|
||||
Ort::ThrowOnError(Ort::GetApi().Run(*_session, runOptions,
|
||||
inputNames.data(), inputValues.data(), inputNames.size(),
|
||||
outputNames.data(), outputNames.size(), outputValues.data()));
|
||||
|
||||
NSMutableDictionary<NSString*, ORTValue*>* outputs = [[NSMutableDictionary alloc] init];
|
||||
for (NSUInteger i = 0; i < outputNameArray.count; ++i) {
|
||||
ORTValue* outputValue = [[ORTValue alloc] initWithCAPIOrtValue:outputValues[i] externalTensorData:nil error:error];
|
||||
if (!outputValue) {
|
||||
// clean up remaining C API OrtValues which haven't been wrapped by an ORTValue yet
|
||||
for (NSUInteger j = i; j < outputNameArray.count; ++j) {
|
||||
Ort::GetApi().ReleaseValue(outputValues[j]);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
outputs[outputNameArray[i]] = outputValue;
|
||||
}
|
||||
|
||||
return outputs;
|
||||
} catch (const Ort::Exception& e) {
|
||||
ORTSaveExceptionToError(e, error);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
215
objectivec/src/ort_value.mm
Normal file
215
objectivec/src/ort_value.mm
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#import "src/ort_value_internal.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
|
||||
#include "safeint/SafeInt.hpp"
|
||||
|
||||
#include "core/session/onnxruntime_cxx_api.h"
|
||||
|
||||
#import "src/error_utils.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
namespace {
|
||||
struct ValueTypeInfo {
|
||||
ORTValueType type;
|
||||
ONNXType capi_type;
|
||||
};
|
||||
|
||||
// supported ORT value types
|
||||
// define the mapping from ORTValueType to C API ONNXType here
|
||||
constexpr ValueTypeInfo kValueTypeInfos[]{
|
||||
{ORTValueTypeUnknown, ONNX_TYPE_UNKNOWN},
|
||||
{ORTValueTypeTensor, ONNX_TYPE_TENSOR},
|
||||
};
|
||||
|
||||
struct TensorElementTypeInfo {
|
||||
ORTTensorElementDataType type;
|
||||
ONNXTensorElementDataType capi_type;
|
||||
size_t element_size;
|
||||
};
|
||||
|
||||
// supported ORT tensor element data types
|
||||
// define the mapping from ORTTensorElementDataType to C API
|
||||
// ONNXTensorElementDataType here
|
||||
constexpr TensorElementTypeInfo kElementTypeInfos[]{
|
||||
{ORTTensorElementDataTypeUndefined, ONNX_TENSOR_ELEMENT_DATA_TYPE_UNDEFINED, 0},
|
||||
{ORTTensorElementDataTypeFloat, ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT, sizeof(float)},
|
||||
{ORTTensorElementDataTypeInt32, ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32, sizeof(int32_t)},
|
||||
};
|
||||
|
||||
ORTValueType CAPIToPublicValueType(ONNXType capi_type) {
|
||||
const auto it = std::find_if(
|
||||
std::begin(kValueTypeInfos), std::end(kValueTypeInfos),
|
||||
[capi_type](const auto& type_info) { return type_info.capi_type == capi_type; });
|
||||
if (it == std::end(kValueTypeInfos)) {
|
||||
throw Ort::Exception{"unsupported value type", ORT_NOT_IMPLEMENTED};
|
||||
}
|
||||
return it->type;
|
||||
}
|
||||
|
||||
ONNXTensorElementDataType PublicToCAPITensorElementType(ORTTensorElementDataType type) {
|
||||
const auto it = std::find_if(
|
||||
std::begin(kElementTypeInfos), std::end(kElementTypeInfos),
|
||||
[type](const auto& type_info) { return type_info.type == type; });
|
||||
if (it == std::end(kElementTypeInfos)) {
|
||||
throw Ort::Exception{"unsupported tensor element type", ORT_NOT_IMPLEMENTED};
|
||||
}
|
||||
return it->capi_type;
|
||||
}
|
||||
|
||||
ORTTensorElementDataType CAPIToPublicTensorElementType(ONNXTensorElementDataType capi_type) {
|
||||
const auto it = std::find_if(
|
||||
std::begin(kElementTypeInfos), std::end(kElementTypeInfos),
|
||||
[capi_type](const auto& type_info) { return type_info.capi_type == capi_type; });
|
||||
if (it == std::end(kElementTypeInfos)) {
|
||||
throw Ort::Exception{"unsupported tensor element type", ORT_NOT_IMPLEMENTED};
|
||||
}
|
||||
return it->type;
|
||||
}
|
||||
|
||||
size_t SizeOfCAPITensorElementType(ONNXTensorElementDataType capi_type) {
|
||||
const auto it = std::find_if(
|
||||
std::begin(kElementTypeInfos), std::end(kElementTypeInfos),
|
||||
[capi_type](const auto& type_info) { return type_info.capi_type == capi_type; });
|
||||
if (it == std::end(kElementTypeInfos)) {
|
||||
throw Ort::Exception{"unsupported tensor element type", ORT_NOT_IMPLEMENTED};
|
||||
}
|
||||
return it->element_size;
|
||||
}
|
||||
}
|
||||
|
||||
@interface ORTValue ()
|
||||
|
||||
// pointer to any external tensor data to keep alive for the lifetime of the ORTValue
|
||||
@property(nullable) NSMutableData* externalTensorData;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ORTValue {
|
||||
std::optional<Ort::Value> _value;
|
||||
std::optional<Ort::TypeInfo> _typeInfo;
|
||||
}
|
||||
|
||||
#pragma mark Public
|
||||
|
||||
- (nullable instancetype)initTensorWithData:(NSMutableData*)tensorData
|
||||
elementType:(ORTTensorElementDataType)elementType
|
||||
shape:(NSArray<NSNumber*>*)shape
|
||||
error:(NSError**)error {
|
||||
try {
|
||||
const auto memoryInfo = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
|
||||
const auto ONNXElementType = PublicToCAPITensorElementType(elementType);
|
||||
const auto shapeVector = [shape]() {
|
||||
std::vector<int64_t> result{};
|
||||
result.reserve(shape.count);
|
||||
for (NSNumber* dim in shape) {
|
||||
result.push_back(dim.longLongValue);
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
Ort::Value ortValue = Ort::Value::CreateTensor(
|
||||
memoryInfo, tensorData.mutableBytes, tensorData.length,
|
||||
shapeVector.data(), shapeVector.size(), ONNXElementType);
|
||||
|
||||
self = [self initWithCAPIOrtValue:ortValue.release()
|
||||
externalTensorData:tensorData
|
||||
error:error];
|
||||
} catch (const Ort::Exception& e) {
|
||||
ORTSaveExceptionToError(e, error);
|
||||
self = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)valueType:(ORTValueType*)valueType
|
||||
error:(NSError**)error {
|
||||
try {
|
||||
const auto ortValueType = _typeInfo->GetONNXType();
|
||||
*valueType = CAPIToPublicValueType(ortValueType);
|
||||
return YES;
|
||||
} catch (const Ort::Exception& e) {
|
||||
ORTSaveExceptionToError(e, error);
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)tensorElementType:(ORTTensorElementDataType*)elementType
|
||||
error:(NSError**)error {
|
||||
try {
|
||||
const auto tensorTypeAndShapeInfo = _typeInfo->GetTensorTypeAndShapeInfo();
|
||||
const auto ortElementType = tensorTypeAndShapeInfo.GetElementType();
|
||||
*elementType = CAPIToPublicTensorElementType(ortElementType);
|
||||
return YES;
|
||||
} catch (const Ort::Exception& e) {
|
||||
ORTSaveExceptionToError(e, error);
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (nullable NSArray<NSNumber*>*)tensorShapeWithError:(NSError**)error {
|
||||
try {
|
||||
const auto tensorTypeAndShapeInfo = _typeInfo->GetTensorTypeAndShapeInfo();
|
||||
const std::vector<int64_t> shape = tensorTypeAndShapeInfo.GetShape();
|
||||
NSMutableArray<NSNumber*>* shapeArray = [[NSMutableArray alloc] initWithCapacity:shape.size()];
|
||||
for (size_t i = 0; i < shape.size(); ++i) {
|
||||
shapeArray[i] = @(shape[i]);
|
||||
}
|
||||
return shapeArray;
|
||||
} catch (const Ort::Exception& e) {
|
||||
ORTSaveExceptionToError(e, error);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (nullable NSMutableData*)tensorDataWithError:(NSError**)error {
|
||||
try {
|
||||
const auto tensorTypeAndShapeInfo = _typeInfo->GetTensorTypeAndShapeInfo();
|
||||
const size_t elementCount = tensorTypeAndShapeInfo.GetElementCount();
|
||||
const size_t elementSize = SizeOfCAPITensorElementType(tensorTypeAndShapeInfo.GetElementType());
|
||||
size_t rawDataLength;
|
||||
if (!SafeMultiply(elementCount, elementSize, rawDataLength)) {
|
||||
throw Ort::Exception{"failed to compute tensor data length", ORT_RUNTIME_EXCEPTION};
|
||||
}
|
||||
void* rawData;
|
||||
Ort::ThrowOnError(Ort::GetApi().GetTensorMutableData(*_value, &rawData));
|
||||
return [NSMutableData dataWithBytesNoCopy:rawData
|
||||
length:rawDataLength
|
||||
freeWhenDone:NO];
|
||||
} catch (const Ort::Exception& e) {
|
||||
ORTSaveExceptionToError(e, error);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Internal
|
||||
|
||||
- (nullable instancetype)initWithCAPIOrtValue:(OrtValue*)CAPIOrtValue
|
||||
externalTensorData:(nullable NSMutableData*)externalTensorData
|
||||
error:(NSError**)error {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
try {
|
||||
_value = Ort::Value{CAPIOrtValue};
|
||||
_typeInfo = _value->GetTypeInfo();
|
||||
_externalTensorData = externalTensorData;
|
||||
} catch (const Ort::Exception& e) {
|
||||
ORTSaveExceptionToError(e, error);
|
||||
self = nil;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (Ort::Value&)CXXAPIOrtValue {
|
||||
return *_value;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
20
objectivec/src/ort_value_internal.h
Normal file
20
objectivec/src/ort_value_internal.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#import "onnxruntime/ort_value.h"
|
||||
|
||||
#include "core/session/onnxruntime_cxx_api.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ORTValue ()
|
||||
|
||||
- (nullable instancetype)initWithCAPIOrtValue:(OrtValue*)CAPIOrtValue
|
||||
externalTensorData:(nullable NSMutableData*)externalTensorData
|
||||
error:(NSError**)error NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (Ort::Value&)CXXAPIOrtValue;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
24
objectivec/test/ort_env_test.mm
Normal file
24
objectivec/test/ort_env_test.mm
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "onnxruntime/ort_env.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ORTEnvTest : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation ORTEnvTest
|
||||
|
||||
- (void)testInitOk {
|
||||
NSError* err = nil;
|
||||
ORTEnv* env = [[ORTEnv alloc] initWithError:&err];
|
||||
XCTAssertNotNil(env);
|
||||
XCTAssertNil(err);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
162
objectivec/test/ort_session_test.mm
Normal file
162
objectivec/test/ort_session_test.mm
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "onnxruntime/ort_env.h"
|
||||
#import "onnxruntime/ort_session.h"
|
||||
#import "onnxruntime/ort_value.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ORTSessionTest : XCTestCase
|
||||
|
||||
@property(readonly, nullable) ORTEnv* ortEnv;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ORTSessionTest
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
|
||||
self.continueAfterFailure = NO;
|
||||
|
||||
_ortEnv = [[ORTEnv alloc] initWithError:nil];
|
||||
XCTAssertNotNil(_ortEnv);
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
_ortEnv = nil;
|
||||
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
+ (NSString*)getTestDataWithRelativePath:(NSString*)relativePath {
|
||||
NSString* testDataDir = [NSString stringWithFormat:@"%@/Contents/Resources/testdata",
|
||||
[[NSBundle bundleForClass:[ORTSessionTest class]] bundlePath]];
|
||||
return [testDataDir stringByAppendingString:relativePath];
|
||||
}
|
||||
|
||||
// model with an Add op
|
||||
// inputs: A, B
|
||||
// output: C = A + B
|
||||
+ (NSString*)getAddModelPath {
|
||||
return [ORTSessionTest getTestDataWithRelativePath:@"/single_add.onnx"];
|
||||
}
|
||||
|
||||
+ (NSMutableData*)dataWithScalarFloat:(float)value {
|
||||
NSMutableData* data = [[NSMutableData alloc] initWithBytes:&value length:sizeof(value)];
|
||||
return data;
|
||||
}
|
||||
|
||||
+ (ORTValue*)ortValueWithScalarFloatData:(NSMutableData*)data {
|
||||
NSArray<NSNumber*>* shape = @[ @1 ];
|
||||
NSError* err = nil;
|
||||
ORTValue* ort_value = [[ORTValue alloc] initTensorWithData:data
|
||||
elementType:ORTTensorElementDataTypeFloat
|
||||
shape:shape
|
||||
error:&err];
|
||||
XCTAssertNotNil(ort_value);
|
||||
XCTAssertNil(err);
|
||||
return ort_value;
|
||||
}
|
||||
|
||||
- (void)testInitAndRunWithPreallocatedOutputOk {
|
||||
NSMutableData* a_data = [ORTSessionTest dataWithScalarFloat:1.0f];
|
||||
NSMutableData* b_data = [ORTSessionTest dataWithScalarFloat:2.0f];
|
||||
NSMutableData* c_data = [ORTSessionTest dataWithScalarFloat:0.0f];
|
||||
|
||||
ORTValue* a = [ORTSessionTest ortValueWithScalarFloatData:a_data];
|
||||
ORTValue* b = [ORTSessionTest ortValueWithScalarFloatData:b_data];
|
||||
ORTValue* c = [ORTSessionTest ortValueWithScalarFloatData:c_data];
|
||||
|
||||
NSError* err = nil;
|
||||
ORTSession* session = [[ORTSession alloc] initWithEnv:self.ortEnv
|
||||
modelPath:[ORTSessionTest getAddModelPath]
|
||||
error:&err];
|
||||
XCTAssertNotNil(session);
|
||||
XCTAssertNil(err);
|
||||
|
||||
BOOL run_result = [session runWithInputs:@{@"A" : a, @"B" : b}
|
||||
outputs:@{@"C" : c}
|
||||
error:&err];
|
||||
XCTAssertTrue(run_result);
|
||||
XCTAssertNil(err);
|
||||
|
||||
const float c_expected = 3.0f;
|
||||
float c_actual;
|
||||
memcpy(&c_actual, c_data.bytes, sizeof(float));
|
||||
XCTAssertEqual(c_actual, c_expected);
|
||||
}
|
||||
|
||||
- (void)testInitAndRunOk {
|
||||
NSMutableData* a_data = [ORTSessionTest dataWithScalarFloat:1.0f];
|
||||
NSMutableData* b_data = [ORTSessionTest dataWithScalarFloat:2.0f];
|
||||
|
||||
ORTValue* a = [ORTSessionTest ortValueWithScalarFloatData:a_data];
|
||||
ORTValue* b = [ORTSessionTest ortValueWithScalarFloatData:b_data];
|
||||
|
||||
NSError* err = nil;
|
||||
ORTSession* session = [[ORTSession alloc] initWithEnv:self.ortEnv
|
||||
modelPath:[ORTSessionTest getAddModelPath]
|
||||
error:&err];
|
||||
XCTAssertNotNil(session);
|
||||
XCTAssertNil(err);
|
||||
|
||||
NSDictionary<NSString*, ORTValue*>* outputs =
|
||||
[session runWithInputs:@{@"A" : a, @"B" : b}
|
||||
outputNames:[NSSet setWithArray:@[ @"C" ]]
|
||||
error:&err];
|
||||
XCTAssertNotNil(outputs);
|
||||
XCTAssertNil(err);
|
||||
|
||||
ORTValue* c_output = outputs[@"C"];
|
||||
XCTAssertNotNil(c_output);
|
||||
|
||||
NSData* c_data = [c_output tensorDataWithError:&err];
|
||||
XCTAssertNotNil(c_data);
|
||||
XCTAssertNil(err);
|
||||
|
||||
const float c_expected = 3.0f;
|
||||
float c_actual;
|
||||
memcpy(&c_actual, c_data.bytes, sizeof(float));
|
||||
XCTAssertEqual(c_actual, c_expected);
|
||||
}
|
||||
|
||||
- (void)testInitFailsWithInvalidPath {
|
||||
NSString* invalid_model_path = [ORTSessionTest getTestDataWithRelativePath:@"/invalid/path/to/model.onnx"];
|
||||
NSError* err = nil;
|
||||
ORTSession* session = [[ORTSession alloc] initWithEnv:self.ortEnv
|
||||
modelPath:invalid_model_path
|
||||
error:&err];
|
||||
XCTAssertNil(session);
|
||||
XCTAssertNotNil(err);
|
||||
}
|
||||
|
||||
- (void)testRunFailsWithInvalidInput {
|
||||
NSMutableData* d_data = [ORTSessionTest dataWithScalarFloat:1.0f];
|
||||
NSMutableData* c_data = [ORTSessionTest dataWithScalarFloat:0.0f];
|
||||
|
||||
ORTValue* d = [ORTSessionTest ortValueWithScalarFloatData:d_data];
|
||||
ORTValue* c = [ORTSessionTest ortValueWithScalarFloatData:c_data];
|
||||
|
||||
NSError* err = nil;
|
||||
ORTSession* session = [[ORTSession alloc] initWithEnv:self.ortEnv
|
||||
modelPath:[ORTSessionTest getAddModelPath]
|
||||
error:&err];
|
||||
XCTAssertNotNil(session);
|
||||
XCTAssertNil(err);
|
||||
|
||||
BOOL run_result = [session runWithInputs:@{@"D" : d}
|
||||
outputs:@{@"C" : c}
|
||||
error:&err];
|
||||
XCTAssertFalse(run_result);
|
||||
XCTAssertNotNil(err);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
78
objectivec/test/ort_value_test.mm
Normal file
78
objectivec/test/ort_value_test.mm
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "onnxruntime/ort_value.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ORTValueTest : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation ORTValueTest
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
|
||||
self.continueAfterFailure = NO;
|
||||
}
|
||||
|
||||
- (void)testInitTensorOk {
|
||||
int32_t value = 42;
|
||||
NSMutableData* data = [[NSMutableData alloc] initWithBytes:&value
|
||||
length:sizeof(int32_t)];
|
||||
NSArray<NSNumber*>* shape = @[ @1 ];
|
||||
|
||||
NSError* err = nil;
|
||||
ORTValue* ortValue = [[ORTValue alloc] initTensorWithData:data
|
||||
elementType:ORTTensorElementDataTypeInt32
|
||||
shape:shape
|
||||
error:&err];
|
||||
XCTAssertNotNil(ortValue);
|
||||
XCTAssertNil(err);
|
||||
|
||||
ORTValueType actualValueType;
|
||||
XCTAssertTrue([ortValue valueType:&actualValueType error:&err]);
|
||||
XCTAssertNil(err);
|
||||
XCTAssertEqual(actualValueType, ORTValueTypeTensor);
|
||||
|
||||
ORTTensorElementDataType actualElementType;
|
||||
XCTAssertTrue([ortValue tensorElementType:&actualElementType error:&err]);
|
||||
XCTAssertNil(err);
|
||||
XCTAssertEqual(actualElementType, ORTTensorElementDataTypeInt32);
|
||||
|
||||
NSArray<NSNumber*>* actualShape = [ortValue tensorShapeWithError:&err];
|
||||
XCTAssertNotNil(actualShape);
|
||||
XCTAssertNil(err);
|
||||
XCTAssertEqualObjects(shape, actualShape);
|
||||
|
||||
NSData* actualData = [ortValue tensorDataWithError:&err];
|
||||
XCTAssertNotNil(actualData);
|
||||
XCTAssertNil(err);
|
||||
XCTAssertEqual(actualData.length, sizeof(int32_t));
|
||||
int32_t actualValue;
|
||||
memcpy(&actualValue, actualData.bytes, sizeof(int32_t));
|
||||
XCTAssertEqual(actualValue, value);
|
||||
}
|
||||
|
||||
- (void)testInitTensorFailsWithDataSmallerThanShape {
|
||||
std::vector<int32_t> values{1, 2, 3, 4};
|
||||
NSMutableData* data = [[NSMutableData alloc] initWithBytes:values.data()
|
||||
length:values.size() * sizeof(int32_t)];
|
||||
NSArray<NSNumber*>* shape = @[ @2, @3 ]; // too large
|
||||
|
||||
NSError* err = nil;
|
||||
ORTValue* ort_value = [[ORTValue alloc] initTensorWithData:data
|
||||
elementType:ORTTensorElementDataTypeInt32
|
||||
shape:shape
|
||||
error:&err];
|
||||
XCTAssertNil(ort_value);
|
||||
XCTAssertNotNil(err);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
16
objectivec/test/testdata/single_add.onnx
vendored
Normal file
16
objectivec/test/testdata/single_add.onnx
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
:S
|
||||
|
||||
A
|
||||
BCAdd"Add SingleAddZ
|
||||
A
|
||||
|
||||
|
||||
Z
|
||||
B
|
||||
|
||||
|
||||
b
|
||||
C
|
||||
|
||||
|
||||
B
|
||||
19
objectivec/test/testdata/single_add_gen.py
vendored
Normal file
19
objectivec/test/testdata/single_add_gen.py
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import onnx
|
||||
from onnx import helper
|
||||
from onnx import TensorProto
|
||||
|
||||
graph = helper.make_graph(
|
||||
[ # nodes
|
||||
helper.make_node("Add", ["A", "B"], ["C"], "Add"),
|
||||
],
|
||||
"SingleAdd", # name
|
||||
[ # inputs
|
||||
helper.make_tensor_value_info('A', TensorProto.FLOAT, [1]),
|
||||
helper.make_tensor_value_info('B', TensorProto.FLOAT, [1]),
|
||||
],
|
||||
[ # outputs
|
||||
helper.make_tensor_value_info('C', TensorProto.FLOAT, [1]),
|
||||
])
|
||||
|
||||
model = helper.make_model(graph)
|
||||
onnx.save(model, r'single_add.onnx')
|
||||
|
|
@ -1,19 +1,21 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "core/session/onnxruntime_c_api.h"
|
||||
#include "core/session/ort_apis.h"
|
||||
#include "core/framework/tensor_shape.h"
|
||||
#include "core/framework/tensor_type_and_shape.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <atomic>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "core/common/safeint.h"
|
||||
#include "core/framework/error_code_helper.h"
|
||||
#include "core/framework/ml_value.h"
|
||||
#include "core/framework/onnxruntime_typeinfo.h"
|
||||
#include "core/framework/sparse_tensor.h"
|
||||
#include "core/framework/tensor_type_and_shape.h"
|
||||
#include "core/framework/tensor_shape.h"
|
||||
#include "core/graph/onnx_protobuf.h"
|
||||
#include "core/framework/error_code_helper.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdexcept>
|
||||
#include <atomic>
|
||||
#include "core/session/onnxruntime_c_api.h"
|
||||
#include "core/session/ort_apis.h"
|
||||
|
||||
using onnxruntime::BFloat16;
|
||||
using onnxruntime::DataTypeImpl;
|
||||
|
|
@ -72,8 +74,10 @@ ORT_API_STATUS_IMPL(OrtApis::GetSymbolicDimensions, _In_ const struct OrtTensorT
|
|||
}
|
||||
|
||||
ORT_API_STATUS_IMPL(OrtApis::GetTensorShapeElementCount, _In_ const OrtTensorTypeAndShapeInfo* this_ptr, _Out_ size_t* out) {
|
||||
*out = static_cast<size_t>(this_ptr->shape.Size());
|
||||
API_IMPL_BEGIN
|
||||
*out = SafeInt<size_t>{this_ptr->shape.Size()};
|
||||
return nullptr;
|
||||
API_IMPL_END
|
||||
}
|
||||
|
||||
struct OrtValue;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,12 @@
|
|||
// Licensed under the MIT License.
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "core/framework/tensor_shape.h"
|
||||
#include "core/session/onnxruntime_c_api.h"
|
||||
|
||||
struct OrtTensorTypeAndShapeInfo {
|
||||
public:
|
||||
ONNXTensorElementDataType type = ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT;
|
||||
|
|
|
|||
|
|
@ -243,6 +243,11 @@ def parse_arguments():
|
|||
"--build_nodejs", action='store_true',
|
||||
help="Build Node.js binding and NPM package.")
|
||||
|
||||
# Objective-C binding
|
||||
parser.add_argument(
|
||||
"--build_objc", action='store_true',
|
||||
help="Build Objective-C binding.")
|
||||
|
||||
# Build a shared lib
|
||||
parser.add_argument(
|
||||
"--build_shared_lib", action='store_true',
|
||||
|
|
@ -656,6 +661,7 @@ def generate_build_tree(cmake_path, source_dir, build_dir, cuda_home, cudnn_home
|
|||
"-Donnxruntime_BUILD_CSHARP=" + ("ON" if args.build_csharp else "OFF"),
|
||||
"-Donnxruntime_BUILD_JAVA=" + ("ON" if args.build_java else "OFF"),
|
||||
"-Donnxruntime_BUILD_NODEJS=" + ("ON" if args.build_nodejs else "OFF"),
|
||||
"-Donnxruntime_BUILD_OBJC=" + ("ON" if args.build_objc else "OFF"),
|
||||
"-Donnxruntime_BUILD_SHARED_LIB=" + ("ON" if args.build_shared_lib else "OFF"),
|
||||
"-Donnxruntime_BUILD_APPLE_FRAMEWORK=" + ("ON" if args.build_apple_framework else "OFF"),
|
||||
"-Donnxruntime_USE_DNNL=" + ("ON" if args.use_dnnl else "OFF"),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,18 @@ jobs:
|
|||
- template: templates/mac-ci.yml
|
||||
parameters:
|
||||
DoNugetPack: 'false'
|
||||
BuildCommand: 'python3 $(Build.SourcesDirectory)/tools/ci_build/build.py --build_dir $(Build.BinariesDirectory) --build_wheel --skip_submodule_sync --parallel --build_shared_lib --build_java --build_nodejs --enable_language_interop_ops --config Debug'
|
||||
BuildCommand: >-
|
||||
python3 $(Build.SourcesDirectory)/tools/ci_build/build.py
|
||||
--config Debug
|
||||
--build_dir $(Build.BinariesDirectory)
|
||||
--build_wheel
|
||||
--skip_submodule_sync
|
||||
--parallel
|
||||
--build_shared_lib
|
||||
--build_java
|
||||
--build_nodejs
|
||||
--build_objc
|
||||
--enable_language_interop_ops
|
||||
# Enable unreleased onnx opsets in CI builds
|
||||
# This facilitates testing the implementation for the new opsets
|
||||
AllowReleasedOpsetOnly: '0'
|
||||
|
|
|
|||
Loading…
Reference in a new issue