Refactor project structure

This commit is contained in:
2026-03-28 19:56:39 +11:00
parent 1d267378b2
commit 8a2e721058
511 changed files with 59 additions and 48 deletions

View File

@@ -0,0 +1,89 @@
#pragma once
#ifndef ANSLIBSLOADER_H
#define ANSLIBSLOADER_H
// ============================================================================
// ANSLibsLoader — Centralized dynamic library loader for ANSCENTER
//
// Consolidates all runtime DLL loading:
// - ONNX Runtime (via EPLoader)
// - TensorRT / cuDNN / CUDA (via NvDynLoader)
// - OpenCV (via CvLoader)
// - OpenVINO + TBB (via OvLoader)
//
// USAGE
// -----
// // At application startup:
// ANSCENTER::ANSLibsLoader::Initialize();
//
// // At application exit (after all engine objects are destroyed):
// ANSCENTER::ANSLibsLoader::Shutdown();
//
// EXPORT MACRO
// ------------
// ANSLIBS_API replaces both ANSCORE_API and ENGINE_API for loader classes.
// Define ANSLIBSLOADER_EXPORTS when building ANSLibsLoader.dll.
// All other projects importing from ANSLibsLoader get __declspec(dllimport).
// ============================================================================
#ifdef _WIN32
# ifdef ANSLIBSLOADER_EXPORTS
# define ANSLIBS_API __declspec(dllexport)
# else
# define ANSLIBS_API __declspec(dllimport)
# endif
#else
# define ANSLIBS_API
#endif
#include "ANSLicense.h" // for ANSCENTER::EngineType enum
#include <string>
namespace ANSCENTER {
class ANSLIBS_API ANSLibsLoader
{
public:
// ── Platform defaults ────────────────────────────────────────────────
#ifdef _WIN32
static constexpr const char* DEFAULT_SHARED_DIR =
"C:\\ProgramData\\ANSCENTER\\Shared";
#else
static constexpr const char* DEFAULT_SHARED_DIR =
"/opt/anscenter/shared";
#endif
// ── Master initialization ────────────────────────────────────────────
/// Calls all sub-loaders in the correct order:
/// 1. Injects shared_dir into DLL search path
/// 2. CvLoader — pre-loads OpenCV
/// 3. OvLoader — pre-loads OpenVINO + TBB
/// 4. NvDynLoader — loads TRT / cuDNN / CUDA (if NVIDIA)
/// 5. EPLoader — loads ONNX Runtime with selected EP
///
/// Thread-safe. Subsequent calls are no-ops.
static void Initialize(
const std::string& shared_dir = DEFAULT_SHARED_DIR,
EngineType preferred = EngineType::AUTO_DETECT);
// ── Master shutdown ──────────────────────────────────────────────────
/// Shuts down all sub-loaders in reverse order.
/// Must be called AFTER all engine/session objects are destroyed.
/// Safe to call multiple times.
static void Shutdown();
// ── Status ───────────────────────────────────────────────────────────
[[nodiscard]] static bool IsInitialized();
// ── Non-copyable / non-movable ───────────────────────────────────────
ANSLibsLoader() = delete;
ANSLibsLoader(const ANSLibsLoader&) = delete;
ANSLibsLoader& operator=(const ANSLibsLoader&) = delete;
private:
static bool s_initialized;
};
} // namespace ANSCENTER
#endif // ANSLIBSLOADER_H

View File

@@ -0,0 +1,62 @@
#pragma once
// ============================================================================
// CvLoader -- Runtime discovery and pre-loading of OpenCV DLLs
//
// OpenCV is normally import-linked (opencv_world4130.lib), but the DLLs
// must be discoverable at process load time. CvLoader ensures the correct
// DLL is found by:
// 1. Searching the ANSCENTER shared directory for versioned candidates
// 2. Injecting the directory into the DLL search path
// 3. Pre-loading the DLL so dependent modules resolve it correctly
//
// Companion modules (opencv_img_hash, opencv_videoio_ffmpeg) are also
// pre-loaded when found alongside the main opencv_world DLL.
// ============================================================================
#include "ANSLibsLoader.h" // ANSLIBS_API
#include "DynLibUtils.h" // LibHandle
#include <string>
#include <vector>
#include <mutex>
// ============================================================================
struct ANSLIBS_API CvInfo
{
std::string dllPath; // Full path to loaded opencv_world DLL
std::string version; // e.g. "4.13.0"
int versionCode = 0;// e.g. 4130
bool loaded = false;
};
// ============================================================================
class ANSLIBS_API CvLoader
{
public:
/// Discover and pre-load OpenCV DLLs.
/// Safe to call multiple times -- subsequent calls are no-ops.
/// @param shared_dir Directory to search first (default: ANSCENTER shared).
/// @param verbose Print discovery results to stdout.
/// @returns Reference to the discovery result.
[[nodiscard]] static const CvInfo& Initialize(
const std::string& shared_dir = ANSCENTER::DynLib::DEFAULT_SHARED_DIR,
bool verbose = true);
/// Release pre-loaded library handles.
static void Shutdown();
/// Query current state.
[[nodiscard]] static const CvInfo& Current() noexcept { return s_info; }
[[nodiscard]] static bool IsInitialized() noexcept { return s_initialized; }
private:
/// Build candidate DLL names, newest first.
static std::vector<std::string> CvWorldCandidates(const std::string& shared_dir);
static std::vector<std::string> CvImgHashCandidates(const std::string& shared_dir);
static std::mutex s_mutex;
static bool s_initialized;
static CvInfo s_info;
static LibHandle s_hCvWorld;
static LibHandle s_hCvImgHash;
};

View File

@@ -0,0 +1,95 @@
#pragma once
#ifndef DYNLIBUTILS_H
#define DYNLIBUTILS_H
// ============================================================================
// DynLibUtils — Shared dynamic library loading utilities
//
// Common infrastructure used by EPLoader, NvDynLoader, CvLoader, OvLoader, etc.
// Consolidates duplicate utility functions that were previously in each loader.
// ============================================================================
#ifdef _WIN32
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# ifndef NOMINMAX
# define NOMINMAX
# endif
# include <windows.h>
using LibHandle = HMODULE;
# define LIB_INVALID nullptr
#else
# include <dlfcn.h>
using LibHandle = void*;
# define LIB_INVALID nullptr
#endif
#include <string>
#include <vector>
#include <iostream>
namespace ANSCENTER {
namespace DynLib {
// ── Platform defaults ────────────────────────────────────────────────────
#ifdef _WIN32
static constexpr const char* DEFAULT_SHARED_DIR =
"C:\\ProgramData\\ANSCENTER\\Shared";
#else
static constexpr const char* DEFAULT_SHARED_DIR =
"/opt/anscenter/shared";
#endif
// ── File / path helpers ──────────────────────────────────────────────────
/// Returns true if @p path is a regular file.
[[nodiscard]] bool FileExists(const std::string& path);
/// Joins base + component with the platform separator.
[[nodiscard]] std::string JoinPath(const std::string& base,
const std::string& component);
// ── DLL search-path injection ────────────────────────────────────────────
/// Adds @p dir to the DLL search path.
/// - Windows: AddDllDirectory() + prepend to %PATH%
/// - Linux: prepend to $LD_LIBRARY_PATH
void InjectDllSearchPath(const std::string& dir);
// ── Library loading ──────────────────────────────────────────────────────
/// Iterates @p candidates, returns the first successfully loaded library.
/// @param loadedPath On success, receives the path/name that was loaded.
/// @param verbose Print discovery results to stdout.
/// @param globalLoad Linux only: use RTLD_GLOBAL (ignored on Windows).
[[nodiscard]] LibHandle FindAndLoad(const std::vector<std::string>& candidates,
std::string& loadedPath,
bool verbose = true,
bool globalLoad = false);
/// Load a single library by path.
/// @param globalLoad Linux only: use RTLD_GLOBAL.
[[nodiscard]] LibHandle LoadLib(const std::string& path, bool globalLoad = false);
/// Free a loaded library handle.
void FreeLib(LibHandle h);
/// Get a symbol address from a loaded library.
[[nodiscard]] void* GetSymbol(LibHandle h, const char* sym);
// ── Type-safe symbol binding ─────────────────────────────────────────────
/// Bind a function pointer from a loaded library handle.
/// @returns true if the symbol was found and bound.
template<typename Fn>
[[nodiscard]] bool Bind(LibHandle h, const char* sym, Fn*& pfn)
{
pfn = reinterpret_cast<Fn*>(GetSymbol(h, sym));
return pfn != nullptr;
}
} // namespace DynLib
} // namespace ANSCENTER
#endif // DYNLIBUTILS_H

View File

@@ -0,0 +1,116 @@
#pragma once
#ifndef EPLOADER_H
#define EPLOADER_H
// ============================================================================
// EPLoader — Dynamic ONNX Runtime loader
//
// Moved from ONNXEngine/ to ANSLibsLoader/ for centralized library management.
// Now exported via ANSLIBS_API from ANSLibsLoader.dll.
// ============================================================================
#include "ANSLibsLoader.h" // ANSLIBS_API, EngineType
#include <string>
#include <mutex>
namespace ANSCENTER {
struct EPInfo {
EngineType type = EngineType::CPU;
std::string name; // "CUDA", "OpenVINO", "DirectML", "CPU"
std::string ortProviderName; // exact ORT provider string
std::string libraryDir; // resolved absolute path to EP DLLs
bool fromSubdir = false; // true = ep/<name>/, false = flat Shared/
};
class ANSLIBS_API EPLoader
{
public:
// ── Platform defaults ────────────────────────────────────────────────
#ifdef _WIN32
static constexpr const char* DEFAULT_SHARED_DIR =
"C:\\ProgramData\\ANSCENTER\\Shared";
#else
static constexpr const char* DEFAULT_SHARED_DIR =
"/opt/anscenter/shared";
#endif
// ── Initialize ───────────────────────────────────────────────────────
/// Call once at application startup (optional — Current() lazy-inits).
///
/// @param shared_dir Root deployment directory.
/// Expected subdirectory layout:
/// <shared_dir>/ep/cuda/onnxruntime.dll
/// <shared_dir>/ep/directml/onnxruntime.dll
/// <shared_dir>/ep/openvino/onnxruntime.dll
/// <shared_dir>/ep/cpu/onnxruntime.dll
/// Falls back to <shared_dir>/onnxruntime.dll if
/// subdirectories are absent (backward compat).
///
/// @param preferred Force a specific EP.
/// Pass EngineType::AUTO_DETECT to let AutoDetect()
/// query ANSLicenseHelper (default).
///
/// @return Const reference to the populated EPInfo.
/// Valid for the lifetime of the process.
///
/// @throws std::runtime_error if onnxruntime.dll cannot be loaded or
/// OrtGetApiBase is not found in the DLL.
[[nodiscard]] static const EPInfo& Initialize(
const std::string& shared_dir = DEFAULT_SHARED_DIR,
EngineType preferred = EngineType::AUTO_DETECT);
// ── Current ──────────────────────────────────────────────────────────
/// Returns the active EPInfo. Calls Initialize() with default arguments
/// on the first invocation (lazy auto-init). Thread-safe.
[[nodiscard]] static const EPInfo& Current();
// ── AutoDetect ───────────────────────────────────────────────────────
/// Delegates to ANSLicenseHelper::CheckHardwareInformation().
/// Priority: NVIDIA > AMD > Intel (OpenVINO) > CPU.
[[nodiscard]] static EngineType AutoDetect();
// ── IsInitialized ────────────────────────────────────────────────────
[[nodiscard]] static bool IsInitialized();
// ── EngineTypeName ───────────────────────────────────────────────────
[[nodiscard]] static const char* EngineTypeName(EngineType type);
// ── SubdirName ───────────────────────────────────────────────────────
/// Returns the ep/ subdirectory name for a given EngineType.
/// e.g. EngineType::NVIDIA_GPU → "cuda"
[[nodiscard]] static const char* SubdirName(EngineType type);
[[nodiscard]] static void* GetOrtApiRaw();
// ── Shutdown ─────────────────────────────────────────────────────────
/// Releases the onnxruntime.dll handle.
/// Call at application exit AFTER all Ort::Session objects are destroyed.
/// Safe to call multiple times.
static void Shutdown();
// ── Non-copyable / non-movable ───────────────────────────────────────
EPLoader() = delete;
EPLoader(const EPLoader&) = delete;
EPLoader& operator=(const EPLoader&) = delete;
private:
static std::string ResolveEPDir(const std::string& shared_dir,
EngineType type);
static void LoadOrtDll(const std::string& ep_dir);
// ── State ────────────────────────────────────────────────────────────
// Defined in EPLoader.cpp only when ANSLIBSLOADER_EXPORTS is set.
static std::mutex s_mutex;
static bool s_initialized;
static EPInfo s_info;
#ifdef _WIN32
static std::string s_temp_ort_path;
static std::string s_temp_dir;
#endif
};
} // namespace ANSCENTER
#endif // EPLOADER_H

View File

@@ -0,0 +1,142 @@
#pragma once
// ============================================================================
// NvDynLoader -- Runtime discovery and loading of NVIDIA TensorRT / CUDA DLLs
//
// Moved from TensorRTAPI/ to ANSLibsLoader/ for centralized library management.
// Now exported via ANSLIBS_API from ANSLibsLoader.dll.
//
// PROBLEM SOLVED
// --------------
// Linking against nvinfer_10.lib / nvonnxparser_10.lib hard-codes the major
// version into the binary's import table. Upgrading TRT 10 -> 11 then forces
// every project to update its .lib references and relink.
//
// SOLUTION
// --------
// NvDynLoader provides the three extern "C" symbols that TRT / ONNX-parser
// inline wrappers call (createInferBuilder_INTERNAL, etc.) as thin stubs
// compiled directly into ANSLibsLoader.dll. At runtime the stubs call through
// function pointers into whichever DLL version is actually installed.
//
// All C++ vtable dispatch (IBuilder, IRuntime, IParser methods) continues to
// work correctly because the objects are created by -- and owned by the vtable
// of -- the DLL that was dynamically loaded.
//
// REQUIRED PROJECT CHANGES
// ------------------------
// Consuming projects:
// REMOVE: nvinfer_10.lib / nvonnxparser_10.lib from linker input
// ADD: ANSLibsLoader.lib
// KEEP: cudart_static.lib (or use NV_DYNAMIC_CUDA for dynamic CUDA RT)
// ============================================================================
#include "ANSLibsLoader.h" // ANSLIBS_API
#include "DynLibUtils.h" // LibHandle
// -- TRT / ONNX-parser API decoration override --------------------------------
// Must appear BEFORE including NvInfer.h / NvOnnxParser.h.
// By default these macros expand to __declspec(dllimport), which would conflict
// with our extern "C" stub definitions in NvDynLoader.cpp. Setting them to
// empty makes all TRT inline-wrapper calls direct, so the linker resolves them
// against our stubs rather than against nvinfer_XX.lib.
//
// The stubs are exported from ANSLibsLoader.dll via the .def file
// (ANSLibsLoader.def), NOT via __declspec(dllexport), to avoid C2375
// linkage conflicts between the NvInfer.h declarations and our definitions.
#ifndef TENSORRTAPI
# define TENSORRTAPI
#endif
#ifndef NVONNXPARSER_API
# define NVONNXPARSER_API
#endif
#include <cuda_runtime.h> // CUDA types (cudaStream_t, cudaDeviceProp, ...)
#include <NvInfer.h> // TRT types (IBuilder, IRuntime, ...)
#include <NvOnnxParser.h> // nvonnxparser types
#include <string>
#include <vector>
// ============================================================================
class ANSLIBS_API NvDynLoader
{
public:
// -- Lifecycle -------------------------------------------------------------
/// Discover and load NVIDIA DLLs at runtime.
/// Safe to call multiple times -- subsequent calls are no-ops.
/// @param verbose Print discovery results to stdout.
/// @returns false if a critical library (TRT or CUDA) could not be loaded.
[[nodiscard]] static bool Initialize(bool verbose = true);
/// Release all loaded library handles. Call at application exit.
static void Shutdown();
[[nodiscard]] static bool IsInitialized() noexcept { return s_initialized; }
// -- Informational ---------------------------------------------------------
[[nodiscard]] static const std::string& TrtDllPath() noexcept { return s_trtPath; }
[[nodiscard]] static const std::string& OnnxDllPath() noexcept { return s_onnxPath; }
[[nodiscard]] static const std::string& CudaDllPath() noexcept { return s_cudaPath; }
[[nodiscard]] static const std::string& CudnnDllPath()noexcept { return s_cudnnPath; }
[[nodiscard]] static int TrtMajor() noexcept { return s_trtMajor; }
// -- TRT factory pointers ---------------------------------------------------
using PfnBuilder = void*(void* logger, int32_t version) noexcept;
using PfnRuntime = void*(void* logger, int32_t version) noexcept;
using PfnRefitter = void*(void* engine, void* logger, int32_t version) noexcept;
using PfnParser = void*(void* network, void* logger, int32_t version) noexcept;
using PfnParserRefitter = void*(void* refitter, void* logger, int32_t version) noexcept;
using PfnGetParserVersion = int() noexcept;
static PfnBuilder* pfn_createInferBuilder_INTERNAL;
static PfnRuntime* pfn_createInferRuntime_INTERNAL;
static PfnRefitter* pfn_createInferRefitter_INTERNAL;
static PfnParser* pfn_createNvOnnxParser_INTERNAL;
static PfnParserRefitter* pfn_createNvOnnxParserRefitter_INTERNAL;
static PfnGetParserVersion* pfn_getNvOnnxParserVersion;
// -- CUDA function pointers (populated only with NV_DYNAMIC_CUDA) -----------
#ifdef NV_DYNAMIC_CUDA
static cudaError_t (*pfn_cudaGetDeviceCount) (int*);
static cudaError_t (*pfn_cudaSetDevice) (int);
static cudaError_t (*pfn_cudaGetDeviceProperties) (cudaDeviceProp*, int);
static cudaError_t (*pfn_cudaDeviceSetLimit) (cudaLimit, size_t);
static cudaError_t (*pfn_cudaDeviceSynchronize) ();
static cudaError_t (*pfn_cudaDeviceGetStreamPriorityRange)(int*, int*);
static cudaError_t (*pfn_cudaMalloc) (void**, size_t);
static cudaError_t (*pfn_cudaFree) (void*);
static cudaError_t (*pfn_cudaMemset) (void*, int, size_t);
static cudaError_t (*pfn_cudaMemGetInfo) (size_t*, size_t*);
static cudaError_t (*pfn_cudaMemcpy) (void*, const void*, size_t, cudaMemcpyKind);
static cudaError_t (*pfn_cudaMemcpyAsync) (void*, const void*, size_t, cudaMemcpyKind, cudaStream_t);
static cudaError_t (*pfn_cudaStreamCreate) (cudaStream_t*);
static cudaError_t (*pfn_cudaStreamCreateWithPriority) (cudaStream_t*, unsigned int, int);
static cudaError_t (*pfn_cudaStreamDestroy) (cudaStream_t);
static cudaError_t (*pfn_cudaStreamSynchronize) (cudaStream_t);
static cudaError_t (*pfn_cudaStreamWaitEvent) (cudaStream_t, cudaEvent_t, unsigned int);
static cudaError_t (*pfn_cudaEventCreate) (cudaEvent_t*);
static cudaError_t (*pfn_cudaEventCreateWithFlags) (cudaEvent_t*, unsigned int);
static cudaError_t (*pfn_cudaEventRecord) (cudaEvent_t, cudaStream_t);
static cudaError_t (*pfn_cudaEventDestroy) (cudaEvent_t);
static const char* (*pfn_cudaGetErrorString) (cudaError_t);
static cudaError_t (*pfn_cudaGetLastError) ();
#endif // NV_DYNAMIC_CUDA
private:
static bool s_initialized;
static int s_trtMajor;
static std::string s_trtPath;
static std::string s_onnxPath;
static std::string s_cudaPath;
static std::string s_cudnnPath;
static LibHandle s_hTrt;
static LibHandle s_hOnnx;
static LibHandle s_hCuda;
static LibHandle s_hCudnn;
// Candidate DLL / SO name lists.
static std::vector<std::string> TrtCandidates();
static std::vector<std::string> OnnxCandidates();
static std::vector<std::string> CudnnCandidates();
static std::vector<std::string> CudaRtCandidates();
};

View File

@@ -0,0 +1,69 @@
#pragma once
// ============================================================================
// OvLoader -- Runtime discovery and pre-loading of OpenVINO DLLs
//
// OpenVINO is normally import-linked (openvino.lib), but the DLLs
// must be discoverable at process load time. OvLoader ensures the correct
// DLLs are found by:
// 1. Pre-loading Intel TBB (dependency of openvino.dll)
// 2. Pre-loading openvino.dll from the ANSCENTER shared directory
// 3. Optionally querying the OpenVINO version via the C API
//
// Plugins (CPU, GPU, NPU) and frontends (IR, ONNX, etc.) are discovered
// automatically by OpenVINO's internal plugin loader from the same directory
// once the shared directory has been injected into the DLL search path.
// ============================================================================
#include "ANSLibsLoader.h" // ANSLIBS_API
#include "DynLibUtils.h" // LibHandle
#include <string>
#include <vector>
#include <mutex>
// ============================================================================
struct ANSLIBS_API OvInfo
{
std::string dllPath; // Full path to loaded openvino.dll
std::string version; // e.g. "2024.6.0" (build number from C API)
bool loaded = false;
};
// ============================================================================
class ANSLIBS_API OvLoader
{
public:
/// Discover and pre-load OpenVINO + TBB DLLs.
/// Safe to call multiple times -- subsequent calls are no-ops.
/// @param shared_dir Directory to search first (default: ANSCENTER shared).
/// @param verbose Print discovery results to stdout.
/// @returns Reference to the discovery result.
[[nodiscard]] static const OvInfo& Initialize(
const std::string& shared_dir = ANSCENTER::DynLib::DEFAULT_SHARED_DIR,
bool verbose = true);
/// Release pre-loaded library handles.
static void Shutdown();
/// Query current state.
[[nodiscard]] static const OvInfo& Current() noexcept { return s_info; }
[[nodiscard]] static bool IsInitialized() noexcept { return s_initialized; }
private:
/// Build candidate DLL names for each component.
static std::vector<std::string> TbbCandidates(const std::string& shared_dir);
static std::vector<std::string> TbbMallocCandidates(const std::string& shared_dir);
static std::vector<std::string> OvCoreCandidates(const std::string& shared_dir);
static std::vector<std::string> OvCApiCandidates(const std::string& shared_dir);
/// Try to extract version string via the ov_get_openvino_version C API.
static std::string TryGetVersion(LibHandle hOvC);
static std::mutex s_mutex;
static bool s_initialized;
static OvInfo s_info;
static LibHandle s_hTbb;
static LibHandle s_hTbbMalloc;
static LibHandle s_hOvCore;
static LibHandle s_hOvC;
};