/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/InferenceSession.h"
#include <prlink.h>
#include <thread>
#include "ErrorList.h"
#include "GeckoProfiler.h"
#include "fmt/format.h"
#include "mozilla/Attributes.h"
#include "mozilla/FileUtils.h"
#include "mozilla/Logging.h"
#include "mozilla/RefPtr.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ONNXBinding.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/Tensor.h"
#include "nsString.h"
#include "nsXPCOMPrivate.h"
mozilla::LazyLogModule gONNXLog(
"GeckoMLONNXNative");
#define LOGV(fmt, ...) \
MOZ_LOG_FMT(gONNXLog, LogLevel::Verbose, fmt,
##__VA_ARGS__)
#define LOGD(fmt, ...) \
MOZ_LOG_FMT(gONNXLog, LogLevel::Debug, fmt,
##__VA_ARGS__)
#define LOGE(fmt, ...) \
MOZ_LOG_FMT(gONNXLog, LogLevel::Error, fmt,
##__VA_ARGS__)
namespace mozilla::dom {
// Initialized when the first InferenceSession is initialized,
// valid until the shutdown of the inference process.
static OrtEnv* sEnv = nullptr;
static OrtApi* sAPI = nullptr;
// RAII wrapper over OrtStatus.
// Takes ownership of a externally allocated OrtStatus* passed at construction.
// Move-only. OrtStatus released through OrtApi::ReleaseStatus.
class AutoOrtStatus {
public:
MOZ_IMPLICIT AutoOrtStatus(OrtStatus* aStatus = nullptr) : mStatus(aStatus) {
MOZ_ASSERT(sAPI);
}
// Prevent copies
AutoOrtStatus(
const AutoOrtStatus&) =
delete;
AutoOrtStatus&
operator=(
const AutoOrtStatus&) =
delete;
// Move semantics
AutoOrtStatus(AutoOrtStatus&& aOther) noexcept
: mStatus(std::exchange(aOther.mStatus, nullptr)) {}
AutoOrtStatus&
operator=(AutoOrtStatus&& aOther) noexcept {
if (this != &aOther) {
Release();
mStatus = std::exchange(aOther.mStatus, nullptr);
}
return *this;
}
~AutoOrtStatus() { Release(); }
explicit operator bool()
const {
return !!mStatus; }
const char* Message()
const {
return sAPI->GetErrorMessage(mStatus); }
void Release() {
if (mStatus) {
sAPI->ReleaseStatus(mStatus);
mStatus = nullptr;
}
}
OrtStatus* mStatus;
};
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(InferenceSession);
NS_IMPL_CYCLE_COLLECTING_ADDREF(InferenceSession)
NS_IMPL_CYCLE_COLLECTING_RELEASE(InferenceSession)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(InferenceSession)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
#define DYLIB_PATH
"onnxruntime"
OrtSessionOptions* ToOrtSessionOption(
const InferenceSessionSessionOptions& aOptions) {
OrtSessionOptions* sessionOptions = nullptr;
AutoOrtStatus status = sAPI->CreateSessionOptions(&sessionOptions);
if (status) {
LOGD(
"CreateSessionOptions error: {}", status.Message());
return nullptr;
}
#define SET_BOOL_ON_SESSION(x) \
do { \
if (aOptions.mEnable
##x) { \
status = sAPI->Enable
##x(sessionOptions); \
}
else { \
status = sAPI->Disable
##x(sessionOptions); \
} \
if (status) { \
LOGE(
"Setter {} (val: {}) error: {}",
#x, aOptions.mEnable
##x, \
status.Message()); \
return nullptr; \
} \
}
while (
0)
LOGD(
"CpuMemArena: {}", aOptions.mEnableCpuMemArena);
SET_BOOL_ON_SESSION(CpuMemArena);
LOGD(
"MemPattern: {}", aOptions.mEnableMemPattern);
SET_BOOL_ON_SESSION(MemPattern);
#define CALL_API(x, ...) \
do { \
status = sAPI->x(sessionOptions, __VA_ARGS__); \
if (status) { \
LOGD(
"SetSessionExecutionMode error: {}", status.Message()); \
return nullptr; \
} \
}
while (
0);
LOGD(
"Session execution mode: {}", aOptions.mExecutionMode);
CALL_API(SetSessionExecutionMode,
aOptions.mExecutionMode.EqualsASCII(
"parallel")
? ExecutionMode::ORT_PARALLEL
: ExecutionMode::ORT_SEQUENTIAL);
LOGD(
"Inter op num threads: {}", aOptions.mInterOpNumThreads);
CALL_API(SetInterOpNumThreads, aOptions.mInterOpNumThreads);
LOGD(
"Intra op num threads: {}", aOptions.mIntraOpNumThreads);
CALL_API(SetIntraOpNumThreads, aOptions.mIntraOpNumThreads);
CALL_API(SetSessionLogId, aOptions.mLogId.get());
CALL_API(SetSessionLogSeverityLevel, aOptions.mLogSeverityLevel);
CALL_API(SetSessionLogVerbosityLevel, aOptions.mLogVerbosityLevel);
PathString path;
#ifdef XP_WIN
path = NS_ConvertUTF8toUTF16(aOptions.mOptimizedModelFilePath.get());
#else
path = aOptions.mOptimizedModelFilePath.get();
#endif
CALL_API(SetOptimizedModelFilePath, path.get());
GraphOptimizationLevel level = ORT_ENABLE_BASIC;
LOGD(
"Graph optimization level: {}", aOptions.mGraphOptimizationLevel);
if (aOptions.mGraphOptimizationLevel.EqualsASCII(
"all")) {
level = ORT_ENABLE_ALL;
}
else if (aOptions.mGraphOptimizationLevel.EqualsASCII(
"basic")) {
level = ORT_ENABLE_BASIC;
}
else if (aOptions.mGraphOptimizationLevel.EqualsASCII(
"extended")) {
level = ORT_ENABLE_EXTENDED;
}
else if (aOptions.mGraphOptimizationLevel.EqualsASCII(
"all")) {
level = ORT_ENABLE_ALL;
}
CALL_API(SetSessionGraphOptimizationLevel, level);
if (aOptions.mFreeDimensionOverrides.WasPassed()) {
for (
const auto& rec : aOptions.mFreeDimensionOverrides.Value().Entries()) {
LOGD(
"Adding free dimension override for key: {}, value: {}", rec.mKey,
rec.mValue);
CALL_API(AddFreeDimensionOverride, rec.mKey.get(), rec.mValue);
}
}
return sessionOptions;
}
// namespace mozilla::dom
OrtApi* GetOrtAPI() {
#ifdef XP_WIN
PathString path = GetLibraryFilePathname(LXUL_DLL, (PRFuncPtr)&GetOrtAPI);
#else
PathString path = GetLibraryFilePathname(XUL_DLL, (PRFuncPtr)&GetOrtAPI);
#endif
if (path.IsEmpty()) {
LOGE(
"Could not locate XUL library when loading onnxruntime");
return nullptr;
}
nsCOMPtr<nsIFile> libFile;
if (NS_FAILED(NS_NewPathStringLocalFile(path, getter_AddRefs(libFile)))) {
LOGE(
"Could not get path string for local file when loading onnxruntime");
return nullptr;
}
if (NS_FAILED(libFile->SetNativeLeafName(
MOZ_DLL_PREFIX
"onnxruntime" MOZ_DLL_SUFFIX
""_ns))) {
LOGE(
"SetNativeLeavName error when loading onnxruntime");
return nullptr;
}
PRLibSpec lspec;
PathString nativePath = libFile->NativePath();
#ifdef XP_WIN
lspec.type = PR_LibSpec_PathnameU;
lspec.value.pathname_u = nativePath.get();
#else
lspec.type = PR_LibSpec_Pathname;
lspec.value.pathname = nativePath.get();
#endif
#ifdef MOZ_WIDGET_ANDROID
PRLibrary* handle = PR_LoadLibraryWithFlags(lspec, PR_LD_NOW | PR_LD_GLOBAL);
#else
PRLibrary* handle = PR_LoadLibraryWithFlags(lspec, PR_LD_NOW | PR_LD_LOCAL);
#endif
if (!handle) {
PRErrorCode code = PR_GetError();
const char* msg = PR_ErrorToString(code, PR_LANGUAGE_I_DEFAULT);
LOGE(
"Couldn't load onnxruntime shared library ({:x}: {})", PR_GetOSError(),
msg);
return nullptr;
}
using OrtApiBaseFn =
const OrtApiBase* (*)();
auto ortGetApiBaseFnPtr =
reinterpret_cast<OrtApiBaseFn>(PR_FindSymbol(handle,
"OrtGetApiBase"));
if (!ortGetApiBaseFnPtr) {
LOGE(
"Couldn't fetch symbol OrgGetApiBase");
PR_UnloadLibrary(handle);
return nullptr;
}
const OrtApiBase* apiBase = ortGetApiBaseFnPtr();
OrtApi* ortAPI =
const_cast<OrtApi*>(apiBase->GetApi(ORT_API_VERSION));
if (!ortAPI) {
LOGE(
"Couldn't get ahold of the OrtApi pointer");
PR_UnloadLibrary(handle);
return nullptr;
}
return ortAPI;
}
bool InferenceSession::InInferenceProcess(JSContext*, JSObject*) {
if (!ContentChild::GetSingleton()) {
return false;
}
return ContentChild::GetSingleton()->GetRemoteType().Equals(
INFERENCE_REMOTE_TYPE);
}
nsCString InferenceSessionSessionOptionsToString(
const InferenceSessionSessionOptions& aOptions) {
return nsFmtCString(
"EnableCpuMemArena: {}, "
"EnableGraphCapture: {}, "
"EnableMemPattern: {}, "
"EnableProfiling: {}, "
"ExecutionMode: {}, "
"ExecutionProviders: {}, "
"Extra: {}, "
"FreeDimensionOverrides: {}, "
"GraphOptimizationLevel: {}, "
"InterOpNumThreads: {}, "
"IntraOpNumThreads: {}, "
"LogId: {}, "
"LogSeverityLevel: {}, "
"LogVerbosityLevel: {}, "
"OptimizedModelFilePath: {}, "
"PreferredOutputLocation: {}, "
"ProfileFilePrefix: {}",
aOptions.mEnableCpuMemArena, aOptions.mEnableGraphCapture,
aOptions.mEnableMemPattern, aOptions.mEnableProfiling,
aOptions.mExecutionMode,
aOptions.mExecutionProviders.WasPassed() ?
"<passed>" :
"<not passed>",
aOptions.mExtra.WasPassed() ?
"<passed>" :
"<not passed>",
aOptions.mFreeDimensionOverrides.WasPassed() ?
"<passed>"
:
"<not passed>",
aOptions.mGraphOptimizationLevel, aOptions.mInterOpNumThreads,
aOptions.mIntraOpNumThreads, aOptions.mLogId, aOptions.mLogSeverityLevel,
aOptions.mLogVerbosityLevel, aOptions.mOptimizedModelFilePath,
aOptions.mPreferredOutputLocation.WasPassed() ?
"<passed>"
:
"<not passed>",
aOptions.mProfileFilePrefix);
}
OrtCustomThreadHandle WrapProfilerRegister(
void* options,
void (*func)(
void*),
void* param) {
// We don't use options for now
MOZ_ASSERT(!options);
auto wrapperFunc = [func](
void* param) {
PROFILER_REGISTER_THREAD(
"onnx_worker");
LOGD(
"Starting thread");
(static_cast<OrtThreadWorkerFn>(func))(param);
};
auto* t =
new std::thread(wrapperFunc, param);
return reinterpret_cast<OrtCustomThreadHandle>(t);
}
void WrapProfilerUnregister(OrtCustomThreadHandle thread) {
LOGD(
"Joining thread");
std::thread* t = (std::thread*)thread;
t->join();
delete t;
}
RefPtr<Promise> InferenceSession::Create(
GlobalObject& aGlobal,
const UTF8StringOrUint8Array& aUriOrBuffer,
const InferenceSessionSessionOptions& aOptions, ErrorResult& aRv) {
LOGD(
"{}", __PRETTY_FUNCTION__);
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
RefPtr<Promise> p = Promise::Create(global, aRv);
RefPtr<InferenceSession> session =
new InferenceSession(aGlobal);
session->Init(p, aUriOrBuffer, aOptions);
return p;
}
void InferenceSession::Init(
const RefPtr<Promise>& aPromise,
const UTF8StringOrUint8Array& aUriOrBuffer,
const InferenceSessionSessionOptions& aOptions) {
LOGD(
"InferenceSession::Init called with a {}",
aUriOrBuffer.IsUTF8String() ?
"string" :
"buffer");
if (!sEnv) {
sAPI = GetOrtAPI();
if (!sAPI) {
LOGD(
"Couldn't get ahold of ORT API");
// Use a distinguishable error so JS callers can recognize that the
// native runtime is unavailable on this machine and fall back to the
// wasm onnx backend (see MLEngineChild's best-onnx handling).
// KEEP IN SYNC: MLEngineChild.sys.mjs matches this message string to
// cache the wasm fallback decision.
aPromise->MaybeRejectWithNotSupportedError(
"onnxruntime shared library could not be loaded");
return;
}
OrtThreadingOptions* threadingOptions;
AutoOrtStatus status = sAPI->CreateThreadingOptions(&threadingOptions);
if (status) {
LOGD(
"CreateThreadingOptions error");
aPromise->MaybeRejectWithUndefined();
return;
}
status = sAPI->SetGlobalCustomCreateThreadFn(threadingOptions,
WrapProfilerRegister);
if (status) {
LOGD(
"SetGlobalCustomCreateThreadFn error");
aPromise->MaybeRejectWithUndefined();
return;
}
status = sAPI->SetGlobalCustomJoinThreadFn(threadingOptions,
WrapProfilerUnregister);
if (status) {
LOGD(
"SetGlobalCustomJoinThreadFn error");
aPromise->MaybeRejectWithUndefined();
return;
}
status = sAPI->SetGlobalInterOpNumThreads(
threadingOptions, AssertedCast<
int>(aOptions.mInterOpNumThreads));
if (status) {
LOGD(
"SetGlobalInterOpNumThreads error");
aPromise->MaybeRejectWithUndefined();
return;
}
status = sAPI->SetGlobalIntraOpNumThreads(
threadingOptions, AssertedCast<
int>(aOptions.mIntraOpNumThreads));
if (status) {
LOGD(
"SetGlobalIntraOpNumThreads error");
aPromise->MaybeRejectWithUndefined();
return;
}
status = sAPI->SetGlobalDenormalAsZero(threadingOptions);
if (status) {
LOGD(
"SetGlobalDenormalsAreZero error");
aPromise->MaybeRejectWithUndefined();
return;
}
status = sAPI->SetGlobalSpinControl(threadingOptions,
0);
if (status) {
LOGD(
"SetGlobalSpinControl error");
aPromise->MaybeRejectWithUndefined();
return;
}
status = sAPI->CreateEnvWithGlobalThreadPools(
ORT_LOGGING_LEVEL_FATAL,
"my_env", threadingOptions, &sEnv);
if (status) {
LOGD(
"CreateEnv error: {}", status.Message());
MOZ_CRASH(
"Init CreateEnv");
}
LOGD(
"CreateEnv OK");
}
mOptions = ToOrtSessionOption(aOptions);
AutoOrtStatus status = sAPI->DisablePerSessionThreads(mOptions);
if (status) {
LOGD(
"DisablePerSessionThreads error: {}", status.Message());
}
OrtSession* session = nullptr;
if (aUriOrBuffer.IsUTF8String()) {
LOGE(
"Passing a URI to a model isn't implemented, pass the bytes directly");
aPromise->MaybeRejectWithNotSupportedError(
"Not implemented");
return;
}
aUriOrBuffer.GetAsUint8Array().ProcessFixedData(
[&](
const Span<uint8_t>& aData) {
AUTO_PROFILER_MARKER_UNTYPED(
"CreateSessionFromArray", ML_SETUP, {});
status = sAPI->CreateSessionFromArray(
sEnv, aData.data(), aData.Length(), mOptions, &session);
});
if (status) {
LOGD(
"CreateSession error: {}", status.Message());
MOZ_CRASH(
"CreateSession error");
}
LOGD(
"Successfully created ONNX Runtime session.");
mSession = session;
aPromise->MaybeResolve(this);
}
nsCString FeedsToString(
const Record<nsCString, OwningNonNull<Tensor>>& aFeeds) {
nsCString rv;
for (
const auto& input : aFeeds.Entries()) {
rv.AppendFmt(
"[{}: {}],", input.mKey, input.mValue->ToString().get());
}
return rv;
}
already_AddRefed<Promise> InferenceSession::Run(
const Record<nsCString, OwningNonNull<Tensor>>& feeds,
const InferenceSessionRunOptions& options, ErrorResult& aRv) {
LOGD(
"{} {}", __PRETTY_FUNCTION__, fmt::ptr(this));
RefPtr<Promise> p = Promise::Create(GetParentObject(), aRv);
if (!mSession) {
LOGD(
"runInference: session pointer is null.");
}
if (!sAPI || !sEnv) {
LOGD(
"Need API {} and Env {} here", fmt::ptr(sAPI), fmt::ptr(sEnv));
MOZ_CRASH(
"In run");
p->MaybeReject(NS_ERROR_UNEXPECTED);
return p.forget();
}
OrtMemoryInfo* memoryInfo = nullptr;
auto guard = MakeScopeExit([&] { sAPI->ReleaseMemoryInfo(memoryInfo); });
AutoOrtStatus status = sAPI->CreateCpuMemoryInfo(
OrtArenaAllocator, OrtMemTypeDefault, &memoryInfo);
if (status) {
LOGD(
"CreateCpuMemoryInfo failed: {}", status.Message());
p->MaybeReject(NS_ERROR_UNEXPECTED);
return p.forget();
}
LOGD(
"Inputs:");
nsTArray<OrtValue*> inputValues;
auto scope = MakeScopeExit([&] {
for (
auto& v : inputValues) {
sAPI->ReleaseValue(v);
}
});
for (
const auto& input : feeds.Entries()) {
OrtValue* inputOrt = nullptr;
const auto& val = input.mValue;
AutoTArray<int64_t,
16> dims64;
for (uint32_t i =
0; i < val->DimsSize(); i++) {
dims64.AppendElement(val->Dims()[i]);
}
LOGD(
"{}: {}", input.mKey.get(), val->ToString().get());
AUTO_PROFILER_MARKER_FMT(
"CreateTensorWithDataAsOrtValue", ML_INFERENCE, {},
"{}", input.mKey.get());
status = sAPI->CreateTensorWithDataAsOrtValue(
memoryInfo, val->Data(), val->Size(), dims64.Elements(),
val->DimsSize(), val->Type(), &inputOrt);
if (status) {
LOGD(
"CreateTensorWithDataAsOrtValue for input_ids {} failed: {}",
input.mKey, status.Message());
p->MaybeReject(NS_ERROR_UNEXPECTED);
return p.forget();
}
inputValues.AppendElement(inputOrt);
}
nsTArray<nsCString> inputNames;
nsTArray<
const char*> inputNamesPtrs;
GetNames(inputNames, NameDirection::Input);
for (
const auto& name : inputNames) {
inputNamesPtrs.AppendElement(name.get());
}
nsTArray<nsCString> outputNames;
nsTArray<
const char*> outputNamesPtrs;
GetNames(outputNames, NameDirection::Output);
LOGD(
"Outputs names:");
for (
const auto& name : outputNames) {
LOGD(
"- {}", name.get());
outputNamesPtrs.AppendElement(name.get());
}
nsTArray<OrtValue*> outputs;
outputs.SetLength(outputNames.Length());
for (uint32_t i =
0; i < outputNames.Length(); i++) {
outputs[i] = nullptr;
}
OrtValue** ptr = outputs.Elements();
{
AUTO_PROFILER_MARKER_UNTYPED(
"Ort::Run", ML_INFERENCE, {});
status = sAPI->Run(mSession,
nullptr,
// Run options
inputNamesPtrs.Elements(), inputValues.Elements(),
inputNamesPtrs.Length(), outputNamesPtrs.Elements(),
outputNamesPtrs.Length(), ptr);
}
if (status) {
LOGD(
"Session Run failed: {}", status.Message());
p->MaybeReject(NS_ERROR_UNEXPECTED);
return p.forget();
}
Record<nsCString, OwningNonNull<Tensor>> rv;
for (size_t i =
0; i < outputs.Length(); i++) {
TimeStamp start = TimeStamp::Now();
// outputData has the same lifetime as output[i]. For now, the actual data
// is copied into the Tensor object below. This copy will be removed in the
// future.
uint8_t* outputData = nullptr;
status = sAPI->GetTensorMutableData(outputs[i], (
void**)&outputData);
if (status) {
LOGD(
"GetTensorMutableData failed: {}", status.Message());
p->MaybeReject(NS_ERROR_UNEXPECTED);
return p.forget();
}
OrtTypeInfo* typeInfo;
status = sAPI->SessionGetOutputTypeInfo(mSession, i, &typeInfo);
if (status) {
LOGD(
"GetOutputTypeInfo failed: {}", status.Message());
p->MaybeReject(NS_ERROR_UNEXPECTED);
return p.forget();
}
OrtTensorTypeAndShapeInfo* typeAndShapeInfo;
status = sAPI->GetTensorTypeAndShape(outputs[i], &typeAndShapeInfo);
if (status) {
LOGD(
"GetTensorTypeAndShape failed: {}", status.Message());
p->MaybeReject(NS_ERROR_UNEXPECTED);
return p.forget();
}
ONNXType type;
status = sAPI->GetOnnxTypeFromTypeInfo(typeInfo, &type);
if (status) {
LOGD(
"GetOnnxTypeFromTypeInfo failed: {}", status.Message());
p->MaybeReject(NS_ERROR_UNEXPECTED);
return p.forget();
}
MOZ_ASSERT(type == ONNX_TYPE_TENSOR);
ONNXTensorElementDataType outputTensorType;
status = sAPI->GetTensorElementType(typeAndShapeInfo, &outputTensorType);
if (status) {
LOGD(
"GetTensorElementType failed: {}", status.Message());
p->MaybeReject(NS_ERROR_UNEXPECTED);
return p.forget();
}
size_t dimCount;
status = sAPI->GetDimensionsCount(typeAndShapeInfo, &dimCount);
if (status) {
LOGD(
"GetDimensionsCount failed: {}", status.Message());
p->MaybeReject(NS_ERROR_UNEXPECTED);
return p.forget();
}
AutoTArray<int64_t,
16> dims;
dims.SetLength(dimCount);
status = sAPI->GetDimensions(typeAndShapeInfo, dims.Elements(), dimCount);
size_t outputSize =
1;
for (size_t d =
0; d < dimCount; ++d) {
outputSize *= dims[d];
}
// TODO skip this copy by using CreateTensorWithDataAsOrtValue
nsTArray<uint8_t> output;
output.AppendElements(outputData,
outputSize * Tensor::DataTypeSize(outputTensorType));
GlobalObject global(mCtx, GetParentObject()->GetGlobalJSObject());
auto outputTensor = MakeRefPtr<Tensor>(global, outputTensorType,
std::move(output), std::move(dims));
AUTO_PROFILER_MARKER_FMT(
"Output tensor", ML_INFERENCE,
MarkerOptions(MarkerTiming::IntervalUntilNowFrom(start)),
"{}: {}",
outputNames[i], outputTensor->ToString().get());
sAPI->ReleaseTensorTypeAndShapeInfo(typeAndShapeInfo);
auto elem = rv.Entries().AppendElement();
elem->mKey = outputNames[i];
elem->mValue = outputTensor;
}
p->MaybeResolve(rv);
return p.forget();
}
void InferenceSession::Destroy() {
LOGD(
"{} {}", __PRETTY_FUNCTION__, fmt::ptr(this));
if (mSession) {
sAPI->ReleaseSession(mSession);
mSession = nullptr;
}
if (mOptions) {
sAPI->ReleaseSessionOptions(mOptions);
mOptions = nullptr;
}
}
already_AddRefed<Promise> InferenceSession::ReleaseSession() {
LOGD(
"{} {}", __PRETTY_FUNCTION__, fmt::ptr(this));
Destroy();
RefPtr<Promise> p = Promise::CreateInfallible(mGlobal);
p->MaybeResolveWithUndefined();
return p.forget();
}
void InferenceSession::StartProfiling() {
LOGD(
"{} {}", __PRETTY_FUNCTION__, fmt::ptr(this));
}
void InferenceSession::EndProfiling() {
LOGD(
"{} {}", __PRETTY_FUNCTION__, fmt::ptr(this));
}
void InferenceSession::GetNames(nsTArray<nsCString>& aRetVal,
NameDirection aDirection)
const {
const char* NameDirection2String[
2] = {
"Input",
"Output"};
if (!mSession) {
return;
}
size_t nameCount =
0;
AutoOrtStatus status;
if (aDirection == NameDirection::Input) {
status = sAPI->SessionGetInputCount(mSession, &nameCount);
}
else {
status = sAPI->SessionGetOutputCount(mSession, &nameCount);
}
if (status) {
LOGD(
"SessionGet{}Count failed: ",
NameDirection2String[static_cast<
int>(aDirection)], status.Message());
return;
}
OrtAllocator* allocator = nullptr;
status = sAPI->GetAllocatorWithDefaultOptions(&allocator);
if (status) {
LOGD(
"GetAllocatorWithDefaultOptions failed: {}", status.Message());
return;
}
aRetVal.SetCapacity(nameCount);
for (size_t i =
0; i < nameCount; i++) {
// Allocated by onnxruntiem, must be freed by AllocatorFree
char* name = nullptr;
if (aDirection == NameDirection::Input) {
status = sAPI->SessionGetInputName(mSession, i, allocator, &name);
}
else {
status = sAPI->SessionGetOutputName(mSession, i, allocator, &name);
}
if (status) {
LOGD(
"SessionGet{}Name failed: ",
NameDirection2String[static_cast<
int>(aDirection)],
status.Message());
continue;
}
aRetVal.AppendElement(name);
status = sAPI->AllocatorFree(allocator, name);
if (status) {
LOGD(
"AllocatorFree failed: ", status.Message());
continue;
}
}
}
void InferenceSession::GetInputNames(nsTArray<nsCString>& aRetVal)
const {
LOGD(
"{} {}", __PRETTY_FUNCTION__, fmt::ptr(this));
GetNames(aRetVal, NameDirection::Input);
if (MOZ_LOG_TEST(gONNXLog, LogLevel::Debug)) {
for (
auto& name : aRetVal) {
LOGD(
"- {}", name);
}
}
}
void InferenceSession::GetOutputNames(nsTArray<nsCString>& aRetVal)
const {
LOGD(
"{} {}", __PRETTY_FUNCTION__, fmt::ptr(this));
GetNames(aRetVal, NameDirection::Output);
if (MOZ_LOG_TEST(gONNXLog, LogLevel::Debug)) {
for (
auto& name : aRetVal) {
LOGD(
"- {}", name);
}
}
}
JSObject* InferenceSession::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
return InferenceSession_Binding::Wrap(aCx, this, aGivenProto);
}
}
// namespace mozilla::dom