// ONNX Runtime C API 보조 함수 (onnxruntime 패키지가 제공하지 않는 기능) // ignore_for_file: implementation_imports import 'dart:ffi' as ffi; import 'dart:io'; import 'dart:typed_data'; import 'package:ffi/ffi.dart'; import 'package:onnxruntime/onnxruntime.dart'; import 'package:onnxruntime/src/bindings/onnxruntime_bindings_generated.dart' as bg; final _api = OrtEnv.instance.ortApiPtr.ref; ffi.Pointer? _cpuInfo; ffi.Pointer _memInfo() { if (_cpuInfo != null) return _cpuInfo!; final pp = calloc>(); _check(_api.CreateCpuMemoryInfo.asFunction< bg.OrtStatusPtr Function(int, int, ffi.Pointer>)>()( bg.OrtAllocatorType.OrtDeviceAllocator, bg.OrtMemType.OrtMemTypeCPU, pp)); _cpuInfo = pp.value; calloc.free(pp); return _cpuInfo!; } void _check(bg.OrtStatusPtr status) => OrtStatus.checkOrtStatus(status); OrtValueTensor _create(ffi.Pointer data, int bytes, List shape, int type) { final shapePtr = calloc(shape.isEmpty ? 1 : shape.length); for (var i = 0; i < shape.length; i++) { shapePtr[i] = shape[i]; } final out = calloc>(); _check(_api.CreateTensorWithDataAsOrtValue.asFunction< bg.OrtStatusPtr Function(ffi.Pointer, ffi.Pointer, int, ffi.Pointer, int, int, ffi.Pointer>)>()( _memInfo(), data, bytes, shapePtr, shape.length, type, out)); final v = OrtValueTensor(out.value, data); calloc.free(shapePtr); calloc.free(out); return v; } /// 모델 파일로 세션 만들기. /// /// onnxruntime 1.4.1 의 OrtSession.fromFile 은 Windows 에서 경로를 UTF-8 로 넘겨 실패한다 /// (Windows 의 ORT 는 wchar_t 경로를 받음). 여기서는 플랫폼에 맞는 문자열로 직접 호출한다. OrtSession openSession(String path, {int threads = 1}) { final optPP = calloc>(); _check(_api.CreateSessionOptions.asFunction< bg.OrtStatusPtr Function(ffi.Pointer>)>()(optPP)); final opt = optPP.value; calloc.free(optPP); try { _check(_api.SetIntraOpNumThreads.asFunction< bg.OrtStatusPtr Function(ffi.Pointer, int)>()(opt, threads)); _check(_api.SetInterOpNumThreads.asFunction< bg.OrtStatusPtr Function(ffi.Pointer, int)>()(opt, 1)); _check(_api.SetSessionGraphOptimizationLevel.asFunction< bg.OrtStatusPtr Function(ffi.Pointer, int)>()( opt, bg.GraphOptimizationLevel.ORT_ENABLE_ALL)); // 바인딩은 char* 로 선언되어 있지만 Windows 네이티브는 wchar_t* 를 읽는다 final ffi.Pointer pathPtr = Platform.isWindows ? path.toNativeUtf16().cast() : path.toNativeUtf8().cast(); final sessPP = calloc>(); try { _check(_api.CreateSession.asFunction< bg.OrtStatusPtr Function(ffi.Pointer, ffi.Pointer, ffi.Pointer, ffi.Pointer>)>()( OrtEnv.instance.ptr, pathPtr, opt, sessPP)); return OrtSession.fromAddress(sessPP.value.address); } finally { calloc ..free(pathPtr) ..free(sessPP); } } finally { _api.ReleaseSessionOptions .asFunction)>()(opt); } } /// int64 텐서 (release() 하면 데이터도 해제됨) OrtValueTensor int64Tensor(List data, List shape) { final p = calloc(data.isEmpty ? 1 : data.length); for (var i = 0; i < data.length; i++) { p[i] = data[i]; } return _create(p.cast(), data.length * 8, shape, ONNXTensorElementDataType.int64.value); } /// float 텐서. [shape] 에 0 이 있으면 빈 텐서. OrtValueTensor floatTensor(Float32List data, List shape) { final p = calloc(data.isEmpty ? 1 : data.length); if (data.isNotEmpty) p.asTypedList(data.length).setAll(0, data); return _create(p.cast(), data.length * 4, shape, ONNXTensorElementDataType.float.value); } OrtValueTensor boolTensor(bool v) { final p = calloc(1)..value = v; return _create(p.cast(), 1, [1], ONNXTensorElementDataType.bool.value); } /// 텐서 모양 List tensorShape(OrtValue v) { final infoPP = calloc>(); _check(_api.GetTensorTypeAndShape.asFunction< bg.OrtStatusPtr Function(ffi.Pointer, ffi.Pointer>)>()(v.ptr, infoPP)); final info = infoPP.value; final countP = calloc(); _check(_api.GetDimensionsCount.asFunction< bg.OrtStatusPtr Function( ffi.Pointer, ffi.Pointer)>()(info, countP)); final n = countP.value; final dims = calloc(n == 0 ? 1 : n); _check(_api.GetDimensions.asFunction< bg.OrtStatusPtr Function( ffi.Pointer, ffi.Pointer, int)>()(info, dims, n)); final shape = [for (var i = 0; i < n; i++) dims[i]]; _api.ReleaseTensorTypeAndShapeInfo .asFunction)>()(info); calloc ..free(dims) ..free(countP) ..free(infoPP); return shape; } /// float 텐서 데이터를 복사 없이 읽기 (텐서를 release 하기 전까지만 유효) Float32List floatView(OrtValue v) { final shape = tensorShape(v); final count = shape.fold(1, (a, b) => a * b); final pp = calloc>(); _check(_api.GetTensorMutableData.asFunction< bg.OrtStatusPtr Function(ffi.Pointer, ffi.Pointer>)>()( v.ptr, pp)); final data = pp.value.cast(); calloc.free(pp); return data.asTypedList(count); }