mcap/modules/onnxruntime/module.jai

113 lines
4.5 KiB
Plaintext

// onnxruntime, loaded at run time. Nothing is linked: ort_load finds the library, takes its one
// export, OrtGetApiBase, and from it the OrtApi function table (bindings.jai, from generate.jai).
// A program that imports this module starts without onnxruntime and only fails in ort_load.
//
// Version: see fetch.jai (ORT_VERSION) and <os>/VERSION.txt. The bindings are for ORT_API_VERSION;
// an older library is refused, a newer one serves this version of the table.
ort: struct {
api: *OrtApi;
library_path: string; // the library that was loaded
version: string; // onnxruntime's own version string, such as 1.30.0
}
// Loads onnxruntime from the first folder that has it: beside the running executable, then each
// of folders. A full path is always used: Windows keeps an older onnxruntime.dll in System32 for
// its own use, which a bare name could find first.
ort_load :: (folders: [] string = .[]) -> ok: bool, error: string {
if ort.api return true, "";
candidates: [..] string;
candidates.allocator = temp;
array_add(*candidates, path_strip_filename(get_path_of_running_executable()));
for folders array_add(*candidates, it);
tried: [..] string;
tried.allocator = temp;
for folder: candidates {
path := tprint("%/%", trim_right(folder, "/\\"), ORT_LIBRARY_NAME);
if !file_exists(path) { array_add(*tried, path); continue; }
get_api_base, load_error := ort_open_library(path);
if !get_api_base return false, load_error;
base := get_api_base();
if !base return false, tprint("% returned no API.", path);
version := to_string(base.GetVersionString());
api := base.GetApi(ORT_API_VERSION);
if !api return false, tprint("% is onnxruntime %, which does not offer API version % (these bindings); use the version fetch.jai pins.", path, version, ORT_API_VERSION);
ort.api = api;
ort.library_path = copy_string(path,, context.default_allocator);
ort.version = copy_string(version,, context.default_allocator);
return true, "";
}
return false, tprint("% was not found. Looked for:\n %\nRun `jai fetch.jai` in modules/onnxruntime and rebuild, so the build copies it beside the executable.",
ORT_LIBRARY_NAME, join(..tried, "\n ",, temp));
}
// A failed status as its message, and the status released. ok and "" for success (a null status).
ort_check :: (status: OrtStatusPtr) -> ok: bool, message: string {
if !status return true, "";
message := tprint("%", to_string(ort.api.GetErrorMessage(status)));
ort.api.ReleaseStatus(status);
return false, message;
}
// A path as the C API takes it: UTF-16 on Windows, UTF-8 elsewhere. The bindings were generated on
// Windows, where ORTCHAR_T is a wchar_t, so elsewhere the pointer is cast to the type they expect. Temp.
ort_path :: (path: string) -> *ORTCHAR_T {
#if OS == .WINDOWS {
return utf8_to_wide(path,, temp);
} else {
return cast(*ORTCHAR_T) temp_c_string(path);
}
}
#load "bindings.jai";
#load "session.jai";
#load "cuda.jai";
#scope_module
Get_Api_Base :: #type () -> *OrtApiBase #c_call;
#if OS == .WINDOWS {
ORT_LIBRARY_NAME :: "onnxruntime.dll";
ort_open_library :: (path: string) -> Get_Api_Base, error: string {
// Altered search path: the libraries onnxruntime loads in turn (onnxruntime_providers_shared.dll,
// onnxruntime_providers_cuda.dll) are looked for in its folder.
windows_path := replace(path, "/", "\\",, temp);
module := LoadLibraryExW(utf8_to_wide(windows_path,, temp), null, LOAD_WITH_ALTERED_SEARCH_PATH);
if !module return null, tprint("Could not load % (Windows error %).", path, GetLastError());
get_api_base := cast(Get_Api_Base) GetProcAddress(module, "OrtGetApiBase");
if !get_api_base return null, tprint("% has no OrtGetApiBase; it is not onnxruntime.", path);
return get_api_base, "";
}
#import "Windows";
#import "Windows_Utf8";
} else {
#if OS == .MACOS ORT_LIBRARY_NAME :: "libonnxruntime.dylib";
else ORT_LIBRARY_NAME :: "libonnxruntime.so";
ort_open_library :: (path: string) -> Get_Api_Base, error: string {
handle := dlopen(temp_c_string(path), RTLD_NOW);
if !handle return null, tprint("Could not load %: %", path, to_string(dlerror()));
get_api_base := cast(Get_Api_Base) dlsym(handle, "OrtGetApiBase");
if !get_api_base return null, tprint("% has no OrtGetApiBase; it is not onnxruntime.", path);
return get_api_base, "";
}
#import "POSIX";
}
#import "Basic";
#import "String";
#import "File";
#import "File_Utilities";
#import "System";
#import "Sort";
#import "Atomics";