diff --git a/cmake/onnxruntime_objectivec.cmake b/cmake/onnxruntime_objectivec.cmake index 3299f87620..ecc504dacb 100644 --- a/cmake/onnxruntime_objectivec.cmake +++ b/cmake/onnxruntime_objectivec.cmake @@ -45,6 +45,7 @@ set(OBJC_ROOT "${REPO_ROOT}/objectivec") # 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/ort_coreml_execution_provider.h" "${OBJC_ROOT}/include/ort_enums.h" "${OBJC_ROOT}/include/ort_env.h" "${OBJC_ROOT}/include/ort_session.h" @@ -68,9 +69,15 @@ target_include_directories(onnxruntime_objc PUBLIC "${OBJC_ROOT}/include" PRIVATE - "${ONNXRUNTIME_ROOT}/include/onnxruntime/core/session" + "${ONNXRUNTIME_INCLUDE_DIR}/core/session" "${OBJC_ROOT}") +if(onnxruntime_USE_COREML) + target_include_directories(onnxruntime_objc + PRIVATE + "${ONNXRUNTIME_INCLUDE_DIR}/core/providers/coreml") +endif() + find_library(FOUNDATION_LIB Foundation REQUIRED) target_link_libraries(onnxruntime_objc diff --git a/include/onnxruntime/core/providers/coreml/coreml_provider_factory.h b/include/onnxruntime/core/providers/coreml/coreml_provider_factory.h index 65ab32a10d..f7857a3cdc 100644 --- a/include/onnxruntime/core/providers/coreml/coreml_provider_factory.h +++ b/include/onnxruntime/core/providers/coreml/coreml_provider_factory.h @@ -5,7 +5,7 @@ #include "onnxruntime_c_api.h" // COREMLFlags are bool options we want to set for CoreML EP -// This enum is defined as bit flats, and cannot have negative value +// This enum is defined as bit flags, and cannot have negative value // To generate an uint32_t coreml_flags for using with OrtSessionOptionsAppendExecutionProvider_CoreML below, // uint32_t coreml_flags = 0; // coreml_flags |= COREML_FLAG_USE_CPU_ONLY; diff --git a/include/onnxruntime/core/providers/nnapi/nnapi_provider_factory.h b/include/onnxruntime/core/providers/nnapi/nnapi_provider_factory.h index a2d5f03a38..57a1ba2aad 100644 --- a/include/onnxruntime/core/providers/nnapi/nnapi_provider_factory.h +++ b/include/onnxruntime/core/providers/nnapi/nnapi_provider_factory.h @@ -5,7 +5,7 @@ #include "onnxruntime_c_api.h" // NNAPIFlags are bool options we want to set for NNAPI EP -// This enum is defined as bit flats, and cannot have negative value +// This enum is defined as bit flags, and cannot have negative value // To generate an uint32_t nnapi_flags for using with OrtSessionOptionsAppendExecutionProvider_Nnapi below, // uint32_t nnapi_flags = 0; // nnapi_flags |= NNAPI_FLAG_USE_FP16; diff --git a/objectivec/include/onnxruntime.h b/objectivec/include/onnxruntime.h index 3fa278b4e3..3521eb673f 100644 --- a/objectivec/include/onnxruntime.h +++ b/objectivec/include/onnxruntime.h @@ -4,6 +4,7 @@ // this header contains the entire ONNX Runtime Objective-C API // the headers below can also be imported individually +#import "ort_coreml_execution_provider.h" #import "ort_enums.h" #import "ort_env.h" #import "ort_session.h" diff --git a/objectivec/include/ort_coreml_execution_provider.h b/objectivec/include/ort_coreml_execution_provider.h new file mode 100644 index 0000000000..6e17bbb093 --- /dev/null +++ b/objectivec/include/ort_coreml_execution_provider.h @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#import + +#import "ort_session.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Gets whether the CoreML execution provider is available. + */ +BOOL ORTIsCoreMLExecutionProviderAvailable(); + +/** + * Options for configuring the CoreML execution provider. + */ +@interface ORTCoreMLExecutionProviderOptions : NSObject + +/** + * Whether the CoreML execution provider should run on CPU only. + */ +@property BOOL useCPUOnly; + +/** + * Whether the CoreML execution provider is enabled on subgraphs. + */ +@property BOOL enableOnSubgraphs; + +/** + * Whether the CoreML execution provider is only enabled for devices with Apple + * Neural Engine (ANE). + */ +@property BOOL onlyEnableForDevicesWithANE; + +@end + +@interface ORTSessionOptions (ORTSessionOptionsCoreMLEP) + +/** + * Enables the CoreML execution provider in the session configuration options. + * It is appended to the execution provider list which is ordered by + * decreasing priority. + * + * @param options The CoreML execution provider configuration options. + * @param[out] error Optional error information set if an error occurs. + * @return Whether the provider was enabled successfully. + */ +- (BOOL)appendCoreMLExecutionProviderWithOptions:(ORTCoreMLExecutionProviderOptions*)options + error:(NSError**)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/objectivec/src/ort_coreml_execution_provider.mm b/objectivec/src/ort_coreml_execution_provider.mm new file mode 100644 index 0000000000..5f86bc28df --- /dev/null +++ b/objectivec/src/ort_coreml_execution_provider.mm @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#import "ort_coreml_execution_provider.h" + +#if __has_include("coreml_provider_factory.h") +#define ORT_OBJC_API_COREML_EP_AVAILABLE 1 +#else +#define ORT_OBJC_API_COREML_EP_AVAILABLE 0 +#endif + +#if ORT_OBJC_API_COREML_EP_AVAILABLE +#include "coreml_provider_factory.h" +#endif + +#import "src/error_utils.h" +#import "src/ort_session_internal.h" + +NS_ASSUME_NONNULL_BEGIN + +BOOL ORTIsCoreMLExecutionProviderAvailable() { + return ORT_OBJC_API_COREML_EP_AVAILABLE ? YES : NO; +} + +@implementation ORTCoreMLExecutionProviderOptions + +@end + +@implementation ORTSessionOptions (ORTSessionOptionsCoreMLEP) + +- (BOOL)appendCoreMLExecutionProviderWithOptions:(ORTCoreMLExecutionProviderOptions*)options + error:(NSError**)error { +#if ORT_OBJC_API_COREML_EP_AVAILABLE + try { + const uint32_t flags = + (options.useCPUOnly ? COREML_FLAG_USE_CPU_ONLY : 0) | + (options.enableOnSubgraphs ? COREML_FLAG_ENABLE_ON_SUBGRAPH : 0) | + (options.onlyEnableForDevicesWithANE ? COREML_FLAG_ONLY_ENABLE_DEVICE_WITH_ANE : 0); + Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CoreML( + [self CXXAPIOrtSessionOptions], flags)); + return YES; + } + ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error); +#else // !ORT_OBJC_API_COREML_EP_AVAILABLE + static_cast(options); + ORTSaveCodeAndDescriptionToError(ORT_FAIL, "CoreML execution provider is not enabled.", error); + return NO; +#endif +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/objectivec/src/ort_session.mm b/objectivec/src/ort_session.mm index b34c20c114..b2a16195b4 100644 --- a/objectivec/src/ort_session.mm +++ b/objectivec/src/ort_session.mm @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#import "ort_session_internal.h" +#import "src/ort_session_internal.h" #include #include diff --git a/objectivec/test/ort_session_test.mm b/objectivec/test/ort_session_test.mm index 965df062ac..0ba3321de1 100644 --- a/objectivec/test/ort_session_test.mm +++ b/objectivec/test/ort_session_test.mm @@ -3,6 +3,7 @@ #import +#import "ort_coreml_execution_provider.h" #import "ort_env.h" #import "ort_session.h" #import "ort_value.h" @@ -191,6 +192,29 @@ NS_ASSUME_NONNULL_BEGIN ORTAssertBoolResultUnsuccessful(runResult, err); } +- (void)testAppendCoreMLEP { + NSError* err = nil; + ORTSessionOptions* sessionOptions = [ORTSessionTest makeSessionOptions]; + ORTCoreMLExecutionProviderOptions* coreMLOptions = [[ORTCoreMLExecutionProviderOptions alloc] init]; + coreMLOptions.enableOnSubgraphs = YES; // set an arbitrary option + + BOOL appendResult = [sessionOptions appendCoreMLExecutionProviderWithOptions:coreMLOptions + error:&err]; + + if (!ORTIsCoreMLExecutionProviderAvailable()) { + ORTAssertBoolResultUnsuccessful(appendResult, err); + return; + } + + ORTAssertBoolResultSuccessful(appendResult, err); + + ORTSession* session = [[ORTSession alloc] initWithEnv:self.ortEnv + modelPath:[ORTSessionTest getAddModelPath] + sessionOptions:sessionOptions + error:&err]; + ORTAssertNullableResultSuccessful(session, err); +} + @end NS_ASSUME_NONNULL_END diff --git a/objectivec/test/testdata/single_add.basic.ort b/objectivec/test/testdata/single_add.basic.ort index 7476b9e0d7..d85f2d4e6c 100644 Binary files a/objectivec/test/testdata/single_add.basic.ort and b/objectivec/test/testdata/single_add.basic.ort differ diff --git a/objectivec/test/testdata/single_add.onnx b/objectivec/test/testdata/single_add.onnx index 1c279f0aa5..82e8fe669e 100644 Binary files a/objectivec/test/testdata/single_add.onnx and b/objectivec/test/testdata/single_add.onnx differ diff --git a/objectivec/test/testdata/single_add_gen.py b/objectivec/test/testdata/single_add_gen.py index f72b8471d4..af8722e099 100644 --- a/objectivec/test/testdata/single_add_gen.py +++ b/objectivec/test/testdata/single_add_gen.py @@ -15,5 +15,5 @@ graph = helper.make_graph( helper.make_tensor_value_info('C', TensorProto.FLOAT, [1]), ]) -model = helper.make_model(graph) +model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 12)]) onnx.save(model, r'single_add.onnx')