Creating a fork of the OpenXR Toolkit to support plugin extensions requires architectural modifications to the core detour/hooking layer, a standardized plugin API/ABI, and a dynamic loading mechanism. Because the OpenXR Toolkit intercepts native OpenXR function calls (like xrCreateInstance, xrBeginSession, xrCreateSwapchain) via an OpenXR active runtime interceptor or OpenXR layer (API layer), adding a plugin system lets external modules hook into these calls, add custom UI elements, or manipulate graphics frames.
Architecture Overview for Plugin Support
To transform the OpenXR Toolkit into a modular framework, you need to implement three primary layers:
* Plugin Manager (PluginManager): Scans a designated directory (e.g., ./plugins/) at startup, loads dynamic libraries (.dll), and resolves exported entry points.
* Event & Hook Interceptor Routing: Allows plugins to register hooks for specific OpenXR functions or rendering lifecycle events (e.g., pre/post xrAcquireSwapchainImage, frame rendering hooks).
* C-Compatible Plugin Interface (OpenXRToolkitPlugin.h): Provides a stable ABI using standard C types so third-party developers can write plugins without compiling against the full toolkit source code.
Step 1: Define the Plugin C-API (OpenXRToolkitPlugin.h)
Create a header file that defines the interface contract between your forked toolkit and external plugins.
#pragma once
#ifdef _WIN32
#define EXPORT_API extern “C” __declspec(dllexport)
#else
#define EXPORT_API extern “C” _attribute_((visibility(“default”)))
#endif
#include <openxr/openxr.h>
// Version checking for ABI compatibility
#define OXRT_PLUGIN_API_VERSION 1
struct OXRT_PluginContext {
int apiVersion;
XrInstance instance;
XrSession session;
// Add logging or utility function pointers here if needed
};
struct OXRT_PluginInterface {
int apiVersion;
const char\* pluginName;
// Lifecycle hooks
bool (\*OnLoad)(const OXRT_PluginContext\* context);
void (\*OnUnload)();
// OpenXR Intercept hooks (Optional examples)
XrResult (\*OnCreateInstance)(const XrInstanceCreateInfo\* info, XrInstance\* instance);
void (\*OnPreRenderFrame)(XrSession session);
void (\*OnPostRenderFrame)(XrSession session);
};
// Every plugin must export this function to be recognized
typedef bool(*OXRT_GetPluginInterfaceFunc)(OXRT_PluginInterface* interfaceStruct);
Step 2: Implement the Plugin Manager in the Toolkit
Next, add a module to scan and load these plugins dynamically during the OpenXR instance creation routine (xrCreateInstance).
#include <windows.h>
#include
#include
#include
#include “OpenXRToolkitPlugin.h”
class PluginManager {
private:
std::vector<HMODULE> loadedModules;
std::vector<OXRT_PluginInterface> plugins;
public:
void LoadPlugins(const std::string& directory, const OXRT_PluginContext& context) {
if (!std::filesystem::exists(directory)) return;
for (const auto& entry : std::filesystem::directory_iterator(directory)) {
if (entry.path().extension() == ".dll") {
HMODULE hMod = LoadLibraryW(entry.path().wstring().c_str());
if (hMod) {
auto getInterface = (OXRT_GetPluginInterfaceFunc)GetProcAddress(hMod, "OXRT_GetPluginInterface");
if (getInterface) {
OXRT_PluginInterface pluginIface{};
if (getInterface(&pluginIface) && pluginIface.apiVersion == OXRT_PLUGIN_API_VERSION) {
if (pluginIface.OnLoad && pluginIface.OnLoad(&context)) {
loadedModules.push_back(hMod);
plugins.push_back(pluginIface);
// Log success: pluginIface.pluginName loaded
}
}
}
if (loadedModules.empty() || loadedModules.back() != hMod) {
FreeLibrary(hMod); // Unload if failed initialization
}
}
}
}
}
void InvokePreRender(XrSession session) {
for (auto& plugin : plugins) {
if (plugin.OnPreRenderFrame) {
plugin.OnPreRenderFrame(session);
}
}
}
void UnloadPlugins() {
for (auto& plugin : plugins) {
if (plugin.OnUnload) plugin.OnUnload();
}
for (HMODULE hMod : loadedModules) {
FreeLibrary(hMod);
}
loadedModules.clear();
plugins.clear();
}
};
Step 3: Integrate Plugin Hooks into OpenXR Detours
Modify the existing interception hooks (typically implemented using MinHook or similar hooking frameworks inside the OpenXR Toolkit’s detour layer) to forward events to your loaded plugins.
For example, inside your intercepted xrBeginSession or frame loop handler:
// Global or singleton instance of your plugin manager
extern PluginManager g_PluginManager;
XrResult Hook_xrBeginSession(XrSession session, const XrSessionBeginInfo* beginInfo) {
// Original OpenXR call execution
XrResult result = Real_xrBeginSession(session, beginInfo);
if (XR_SUCCEEDED(result)) {
// Trigger custom startup logic for plugins tied to session creation
}
return result;
}
// Inside your rendering loop thread/detour:
void Tracked_FrameRenderLoop(XrSession session) {
g_PluginManager.InvokePreRender(session);
// ... Toolkit's native upscaling, foveated rendering, or image sharpening logic ...
// Post-render actions can go here
}
Step 4: Structuring the Fork Repository
To properly organize your GitHub fork:
* /openxr-toolkit-core/: Contains the original upstream logic (upscaling, sharpening, hand-tracking wrappers, Menu UI overlay).
* /openxr-toolkit-sdk/: Contains OpenXRToolkitPlugin.h and helper libraries so third-party developers can easily build custom plugins.
* /plugins/: A sample directory containing reference plugins (e.g., an FPS counter plugin, custom shader injection plugin, or telemetry logger).
Step 5: Building a Sample Third-Party Plugin
An extension developer can build a plugin targeting your fork by creating a simple DLL project:
#include “OpenXRToolkitPlugin.h”
#include <windows.h>
bool MyPlugin_OnLoad(const OXRT_PluginContext* context) {
OutputDebugStringA("Custom OpenXR Toolkit Plugin Loaded Successfully!\\n");
return true;
}
void MyPlugin_OnPreRender(XrSession session) {
// Perform custom per-frame logic, telemetry, or adjustments
}
EXPORTS_API bool OXRT_GetPluginInterface(OXRT_PluginInterface* interfaceStruct) {
interfaceStruct->apiVersion = OXRT_PLUGIN_API_VERSION;
interfaceStruct->pluginName = "Sample Telemetry Plugin";
interfaceStruct->OnLoad = MyPlugin_OnLoad;
interfaceStruct->OnPreRenderFrame = MyPlugin_OnPreRender;
return true;
}
Key Considerations & Next Steps
* OpenXR Active Runtime / Layer JSON Manifest: Ensure your toolkit registers itself properly as an active OpenXR API Layer (XR_APILAYER_MOVIES_openxr_toolkit or similar mechanism) so application
s load it seamlessly.
* Thread Safety: Ensure plugin hooks are thread-safe, as OpenXR rendering loops and callbacks often occur on separate graphics threads.