Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions plugin_execution_providers/basic/csharp/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/.vs/
/local_feed/
/Contoso.ML.OnnxRuntime.EP.Basic/runtimes/**/native/*.dll
*.nupkg
Comment thread
adrianlizarraga marked this conversation as resolved.
*.snupkg
**/packages/*
!**/packages/build/
**/bin/*
48 changes: 48 additions & 0 deletions plugin_execution_providers/basic/csharp/BasicPluginEpExample.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Contoso.ML.OnnxRuntime.EP.Basic", "Contoso.ML.OnnxRuntime.EP.Basic\Contoso.ML.OnnxRuntime.EP.Basic.csproj", "{07807D61-E3FC-401E-B947-328645A0A775}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApp", "SampleApp\SampleApp.csproj", "{9725B6C8-5BB5-45C6-9F93-FA4322215154}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{07807D61-E3FC-401E-B947-328645A0A775}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{07807D61-E3FC-401E-B947-328645A0A775}.Debug|Any CPU.Build.0 = Debug|Any CPU
{07807D61-E3FC-401E-B947-328645A0A775}.Debug|x64.ActiveCfg = Debug|Any CPU
{07807D61-E3FC-401E-B947-328645A0A775}.Debug|x64.Build.0 = Debug|Any CPU
{07807D61-E3FC-401E-B947-328645A0A775}.Debug|x86.ActiveCfg = Debug|Any CPU
{07807D61-E3FC-401E-B947-328645A0A775}.Debug|x86.Build.0 = Debug|Any CPU
{07807D61-E3FC-401E-B947-328645A0A775}.Release|Any CPU.ActiveCfg = Release|Any CPU
{07807D61-E3FC-401E-B947-328645A0A775}.Release|Any CPU.Build.0 = Release|Any CPU
{07807D61-E3FC-401E-B947-328645A0A775}.Release|x64.ActiveCfg = Release|Any CPU
{07807D61-E3FC-401E-B947-328645A0A775}.Release|x64.Build.0 = Release|Any CPU
{07807D61-E3FC-401E-B947-328645A0A775}.Release|x86.ActiveCfg = Release|Any CPU
{07807D61-E3FC-401E-B947-328645A0A775}.Release|x86.Build.0 = Release|Any CPU
{9725B6C8-5BB5-45C6-9F93-FA4322215154}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9725B6C8-5BB5-45C6-9F93-FA4322215154}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9725B6C8-5BB5-45C6-9F93-FA4322215154}.Debug|x64.ActiveCfg = Debug|Any CPU
{9725B6C8-5BB5-45C6-9F93-FA4322215154}.Debug|x64.Build.0 = Debug|Any CPU
{9725B6C8-5BB5-45C6-9F93-FA4322215154}.Debug|x86.ActiveCfg = Debug|Any CPU
{9725B6C8-5BB5-45C6-9F93-FA4322215154}.Debug|x86.Build.0 = Debug|Any CPU
{9725B6C8-5BB5-45C6-9F93-FA4322215154}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9725B6C8-5BB5-45C6-9F93-FA4322215154}.Release|Any CPU.Build.0 = Release|Any CPU
{9725B6C8-5BB5-45C6-9F93-FA4322215154}.Release|x64.ActiveCfg = Release|Any CPU
{9725B6C8-5BB5-45C6-9F93-FA4322215154}.Release|x64.Build.0 = Release|Any CPU
{9725B6C8-5BB5-45C6-9F93-FA4322215154}.Release|x86.ActiveCfg = Release|Any CPU
{9725B6C8-5BB5-45C6-9F93-FA4322215154}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using System.Runtime.InteropServices;

namespace Contoso.ML.OnnxRuntime.EP.Basic;

public static class BasicEp
{
/// <summary>
/// Returns the path to the plugin EP library DLL contained by this package.
/// Can be passed to OrtEnv::RegisterExecutionProviderLibrary().
///
/// Note: It is recommended that plugin EP packages provide this information to applications.
/// </summary>
/// <returns>EP library path or empty string if not found</returns>
public static string GetLibraryPath()
{
string rootDir = GetNativeDirectory();
string osArch = $"{GetOSTag()}-{GetArchTag()}";
string candidatePath = Path.Combine(rootDir, "runtimes", osArch, "native", "basic_plugin_ep.dll");

if (File.Exists(candidatePath))
{
return Path.GetFullPath(candidatePath);
}

// Not found
return string.Empty;
Comment thread
edgchen1 marked this conversation as resolved.
Outdated
}

/// <summary>
/// Returns the names of the EPs created by the plugin EP library.
/// Can be used to select a OrtEpDevice from those returned by OrtEnv::GetEpDevices().
///
/// Note: It is recommended that plugin EP packages provide this information to applications.
/// </summary>
/// <returns>Array of EP names</returns>
public static string[] GetEpNames()
{
return ["BasicPluginExecutionProvider"];
}

/// <summary>
/// Returns the name of the one EP supported by this plugin EP library.
///
/// Note: This is a convenience function exposed by plugin EP packages that only have one EP name.
/// </summary>
/// <returns></returns>
public static string GetEpName()
{
return GetEpNames()[0];
}

private static string GetNativeDirectory()
{
var assemblyDir = Path.GetDirectoryName(typeof(BasicEp).Assembly.Location);

// Try returning where this assembly lives (works for framework-dependent)
if (!string.IsNullOrEmpty(assemblyDir) && Directory.Exists(assemblyDir))
return assemblyDir;

// Fallback to AppContext.BaseDirectory (works for single-file/self-contained)
return AppContext.BaseDirectory;
}
private static string GetOSTag()
Comment thread
adrianlizarraga marked this conversation as resolved.
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return "win";
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return "linux";
return "unknown";
}

private static string GetArchTag()
{
return RuntimeInformation.OSArchitecture switch
{
Architecture.X64 => "x64",
Architecture.Arm64 => "arm64",
_ => "unknown"
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<Runtime Condition=" $([MSBuild]::IsOsPlatform('Windows')) AND '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)' == 'X64' ">win-x64</Runtime>
<Runtime Condition=" $([MSBuild]::IsOsPlatform('Windows')) AND '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)' == 'Arm64' ">win-arm64</Runtime>

<!-- Package info -->
<PackageId>Contoso.ML.OnnxRuntime.EP.Basic</PackageId>
<Version>1.0.0</Version>
<Authors>ORT</Authors>
<Company>Microsoft</Company>
<Description>A minimal package for a basic plugin EP.</Description>
<PackageReadmeFile>readme.md</PackageReadmeFile>

<!-- License/Repository -->
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryUrl>https://github.com/microsoft/onnxruntime-inference-examples</RepositoryUrl>
<RepositoryType>git</RepositoryType>

<!-- Include symbols/source for better debugging experience -->
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>

<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
</PropertyGroup>

<ItemGroup>
<!-- Ensure README is included in the package -->
<None Include="readme.md" Pack="true" PackagePath="" />
</ItemGroup>

<!-- Copy EP DLL to output directory under runtimes/*/native/ -->
<ItemGroup Condition="'$(Runtime)' == 'win-x64'">
<None Include="runtimes\win-x64\native\basic_plugin_ep.dll"
Pack="true"
PackagePath="runtimes/win-x64/native/basic_plugin_ep.dll"
CopyToOutputDirectory="PreserveNewest"/>
</ItemGroup>

<ItemGroup Condition="'$(Runtime)' == 'win-arm64'">
<None Include="runtimes\win-arm64\native\basic_plugin_ep.dll"
Pack="true"
PackagePath="runtimes/win-arm64/native/basic_plugin_ep.dll"
CopyToOutputDirectory="PreserveNewest"/>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## Contoso.ML.OnnxRuntime.EP.Basic

Example plugin EP library for use with ONNX Runtime (Microsoft.ML.OnnxRuntime).
68 changes: 68 additions & 0 deletions plugin_execution_providers/basic/csharp/SampleApp/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using Contoso.ML.OnnxRuntime.EP.Basic;
using Microsoft.ML.OnnxRuntime;

class Program
{
static void Main()
{
string epLibPath = BasicEp.GetLibraryPath();
Comment thread
adrianlizarraga marked this conversation as resolved.
string epRegistrationName = "basic_ep_registration";
string epName = BasicEp.GetEpName();

Comment thread
adrianlizarraga marked this conversation as resolved.
if (string.IsNullOrEmpty(epLibPath))
{
Console.Error.WriteLine("ERROR: BasicEp.GetLibraryPath() returned an empty path");
return;
}

var env = OrtEnv.Instance();
env.RegisterExecutionProviderLibrary(epRegistrationName, epLibPath);
Console.WriteLine($"Registered EP library: {epLibPath}");

try
{
// Find the OrtEpDevice for the EP
OrtEpDevice? epDevice = null;
foreach (var d in env.GetEpDevices())
{
if (string.Equals(epName, d.EpName, StringComparison.OrdinalIgnoreCase))
{
epDevice = d;
}
}

if (epDevice == null)
{
Console.Error.WriteLine($"ERROR: Unable to find OrtEpDevice with name {epName}");
return;
}
Console.WriteLine($"Found OrtEpDevice for EP: {epName}");

// Create session with EP
using var sessionOptions = new SessionOptions();
sessionOptions.AppendExecutionProvider(env, [epDevice], new Dictionary<string, string> { });
sessionOptions.AddSessionConfigEntry("session.disable_cpu_ep_fallback", "1"); // Don't run on CPU EP

string inputModelPath = Path.Combine(AppContext.BaseDirectory, "mul.onnx");
Console.WriteLine($"Loading model: {inputModelPath}");

using var session = new InferenceSession(inputModelPath, sessionOptions);

// Run model
float[] inputData = [1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f];
var inputOrtValue = OrtValue.CreateTensorValueFromMemory<float>(inputData, [2, 3]);
Comment thread
adrianlizarraga marked this conversation as resolved.
Outdated
var inputValues = new List<OrtValue> { inputOrtValue, inputOrtValue }.AsReadOnly();
Comment thread
adrianlizarraga marked this conversation as resolved.
Outdated
var inputNames = new List<string> { "x", "y" }.AsReadOnly();
using var runOptions = new RunOptions();

using var outputs = session.Run(runOptions, inputNames, inputValues, session.OutputNames);

Console.WriteLine($"Input: {string.Join(", ", inputData)}");
Console.WriteLine($"Output: {string.Join(", ", outputs[0].GetTensorDataAsSpan<float>().ToArray())}");
}
finally
{
env.UnregisterExecutionProviderLibrary(epRegistrationName);
}
}
}
26 changes: 26 additions & 0 deletions plugin_execution_providers/basic/csharp/SampleApp/SampleApp.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Contoso.ML.OnnxRuntime.EP.Basic" Version="1.0.0" />
<PackageReference Include="Microsoft.ML.OnnxRuntime" Version="1.23.2" />
</ItemGroup>
Comment thread
adrianlizarraga marked this conversation as resolved.

<ItemGroup>
<Content Include="mul.onnx">
Comment thread
edgchen1 marked this conversation as resolved.
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

<!-- Consume nuget package via project reference during development to get live code changes -->
<!-- <ItemGroup>
<ProjectReference Include="..\Contoso.ML.OnnxRuntime.EP.Basic\Contoso.ML.OnnxRuntime.EP.Basic.csproj" />
</ItemGroup> -->

</Project>
6 changes: 6 additions & 0 deletions plugin_execution_providers/basic/csharp/nuget.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="local_feed" value="./local_feed" />
</packageSources>
</configuration>
34 changes: 34 additions & 0 deletions plugin_execution_providers/basic/csharp/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Basic Plugin Execution Provider with C#

## Contents
- `Contoso.ML.OnnxRuntime.EP.Basic/`: Contains files for the basic plugin EP C# NuGet package. `BasicEp.cs` provides helper functions to get the EP library path and the EP name.
- `SampleApp/`: Contains a sample C# application showing example usage of the basic plugin EP C# NuGet package.
- `setup.bat`: Batch script to generate the NuGet package.

## Build Instructions
This example currently only supports Windows x64 and Windows ARM64.

### Build the native plugin EP library

Follow instructions [here](../readme.md#build-instructions) to build the native library.

### Build the C\# NuGet Package

Set the environment variable `BASIC_PLUGIN_EP_LIBRARY_PATH` to the path to the native plugin EP shared library. E.g., `basic_plugin_ep.dll`.

Run `setup.bat` from this directory. Pass the build configuration (e.g., Release or Debug) as an argument.

```
.\setup.bat Release
```

The generated NuGet package will be copied to the `./local_feed` directory.

## Build and run the sample application

Build and run the sample application.

```
dotnet build .\SampleApp\SampleApp.csproj -c Release
dotnet run --project .\SampleApp\SampleApp.csproj -c Release
```
63 changes: 63 additions & 0 deletions plugin_execution_providers/basic/csharp/setup.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
@echo off

REM Builds NuGet package that wraps basic plugin EP

IF "%~1"=="" (
echo ERROR: No build configuration specified.
echo Usage: .\setup.bat [Debug^|Release]
exit /b 1
)

SET "BUILD_CONFIG=%~1"

if "%BASIC_PLUGIN_EP_LIBRARY_PATH%"=="" (
echo ERROR: BASIC_PLUGIN_EP_LIBRARY_PATH environment variable is not set.
exit /b 1
)

if not exist "%BASIC_PLUGIN_EP_LIBRARY_PATH%" (
echo ERROR: EP library "%BASIC_PLUGIN_EP_LIBRARY_PATH%" not found.
exit /b 1
)

set "ARCH=%PROCESSOR_ARCHITECTURE%"
if defined PROCESSOR_ARCHITEW6432 set "ARCH=%PROCESSOR_ARCHITEW6432%"

if /i "%ARCH%"=="AMD64" (
set "DEST_EP_DLL_FOLDER=.\Contoso.ML.OnnxRuntime.EP.Basic\runtimes\win-x64\native\"
) else if /i "%ARCH%"=="ARM64" (
set "DEST_EP_DLL_FOLDER=.\Contoso.ML.OnnxRuntime.EP.Basic\runtimes\win-arm64\native\"
) else (
echo ERROR: Unknown architecture "%ARCH%"
exit /b 1
)

Comment thread
adrianlizarraga marked this conversation as resolved.
if not exist "%DEST_EP_DLL_FOLDER%" (
mkdir "%DEST_EP_DLL_FOLDER%" || (
echo ERROR: Failed to create "%DEST_EP_DLL_FOLDER%".
exit /b 1
)
)

echo Copying EP DLL to "%DEST_EP_DLL_FOLDER%"
copy /Y "%BASIC_PLUGIN_EP_LIBRARY_PATH%" "%DEST_EP_DLL_FOLDER%" >nul

if errorlevel 1 (
echo ERROR: Failed to copy EP library to "%DEST_EP_DLL_FOLDER%".
exit /b 1
)

echo Building NuGet package ("%BUILD_CONFIG%") ...
dotnet build .\Contoso.ML.OnnxRuntime.EP.Basic\Contoso.ML.OnnxRuntime.EP.Basic.csproj -c "%BUILD_CONFIG%"
dotnet pack .\Contoso.ML.OnnxRuntime.EP.Basic\Contoso.ML.OnnxRuntime.EP.Basic.csproj -c "%BUILD_CONFIG%"

set "LOCAL_FEED_FOLDER=local_feed"
if not exist "%LOCAL_FEED_FOLDER%" (
mkdir "%LOCAL_FEED_FOLDER%" || (
echo ERROR: Failed to create "%LOCAL_FEED_FOLDER%"
Comment thread
adrianlizarraga marked this conversation as resolved.
exit /b 1
)
)

copy /Y .\Contoso.ML.OnnxRuntime.EP.Basic\bin\"%BUILD_CONFIG%"\Contoso.ML.OnnxRuntime.EP.Basic.*.nupkg .\local_feed\
copy /Y .\Contoso.ML.OnnxRuntime.EP.Basic\bin\"%BUILD_CONFIG%"\Contoso.ML.OnnxRuntime.EP.Basic.*.snupkg .\local_feed\
Loading