Simplify XeLL context handling

This commit is contained in:
FakeMichau
2026-06-27 15:50:07 +02:00
parent f81ebb9a4e
commit 6aec9c7264
20 changed files with 82 additions and 249 deletions
+9 -25
View File
@@ -67,34 +67,22 @@ bool XeFG_Dx12::CreateSwapchainContext(ID3D12Device* device)
LOG_ERROR("SetLoggingCallback error: {} ({})", magic_enum::enum_name(result), (UINT) result);
}
// if (XeLLProxy::Context() == nullptr)
XeLLProxy::CreateContext(device);
if (XeLLProxy::Context() != nullptr)
// Force fakenvapi to create XeLL for us
if (fakenvapi::forceMode(device, LowLatencyMode::XeLL))
{
xell_sleep_params_t sleepParams = {};
sleepParams.bLowLatencyMode = true;
sleepParams.bLowLatencyBoost = false;
sleepParams.minimumIntervalUs = 0;
auto xellResult = XeLLProxy::SetSleepMode()(XeLLProxy::Context(), &sleepParams);
if (xellResult != XELL_RESULT_SUCCESS)
{
LOG_ERROR("SetSleepMode error: {} ({})", magic_enum::enum_name(xellResult), (UINT) xellResult);
return result;
}
auto fnaResult = fakenvapi::setModeAndContext(XeLLProxy::Context(), LowLatencyMode::XeLL);
LOG_DEBUG("fakenvapi::setModeAndContext: {}", fnaResult);
result = XeFGProxy::SetLatencyReduction()(_swapChainContext, XeLLProxy::Context());
result = XeFGProxy::SetLatencyReduction()(_swapChainContext, fakenvapi::getCurrentContext());
if (result != XEFG_SWAPCHAIN_RESULT_SUCCESS)
{
LOG_ERROR("SetLatencyReduction error: {} ({})", magic_enum::enum_name(result), (UINT) result);
return result;
}
};
}
else
{
LOG_ERROR("Couldn't create XeLL");
return false;
}
createResult = true;
@@ -153,9 +141,6 @@ bool XeFG_Dx12::DestroySwapchainContext()
}
else
{
if (XeLLProxy::Context() != nullptr)
XeLLProxy::DestroyXeLLContext();
State::Instance().currentFGSwapchain = nullptr;
}
}
@@ -1639,7 +1624,6 @@ bool XeFG_Dx12::ReleaseSwapchain(HWND hwnd)
}
ReleaseObjects();
XeLLProxy::DestroyXeLLContext();
if (Config::Instance()->FGUseMutexForSwapchain.value_or_default())
{
@@ -138,6 +138,9 @@ bool InputCommon::update_low_latency_tech(IUnknown* pDevice, std::optional<LowLa
desiredMode = LowLatencyMode::LatencyFlex;
}
if (State::Instance().activeFgOutput == FGOutput::XeFG)
desiredMode = LowLatencyMode::XeLL;
if (activeOutput == desiredMode)
return true;
@@ -730,7 +733,7 @@ xell_result_t InputCommon::pass_xellSetFgEnabled(const InputContext& inputContex
return XELL_RESULT_SUCCESS;
}
xell_result_t InputCommon::pass_xellSetGeneratedFramesCount(const InputContext& inputContext, uint32_t param1,
xell_result_t InputCommon::pass_xellSetGeneratedFramesCount(const InputContext& inputContext, uint32_t frameId,
uint32_t framesCount)
{
if (inputContext.caller == LowLatencyInput::XeLL && activeOutput == LowLatencyMode::XeLL)
@@ -738,7 +741,7 @@ xell_result_t InputCommon::pass_xellSetGeneratedFramesCount(const InputContext&
if (auto current_tech = currently_active_tech.load())
{
auto xell_tech = std::static_pointer_cast<XeLL>(current_tech);
return xell_tech->xellSetGeneratedFramesCount(param1, framesCount);
return xell_tech->xellSetGeneratedFramesCount(frameId, framesCount);
}
return XELL_RESULT_ERROR_UNKNOWN;
+1 -1
View File
@@ -89,6 +89,6 @@ class InputCommon
// async markers can be made
static xell_result_t pass_xellSetDisplayInfo(const InputContext& inputContext, void* displayInfo);
static xell_result_t pass_xellSetFgEnabled(const InputContext& inputContext, uint32_t param1, uint32_t param2);
static xell_result_t pass_xellSetGeneratedFramesCount(const InputContext& inputContext, uint32_t param1,
static xell_result_t pass_xellSetGeneratedFramesCount(const InputContext& inputContext, uint32_t frameId,
uint32_t framesCount);
};
+5 -5
View File
@@ -317,7 +317,7 @@ xell_result_t InputXeLL::SetFgEnabled(xell_input_handle_t context, uint32_t para
return InputCommon::pass_xellSetFgEnabled(context->inputContext, param1, param2);
}
xell_result_t InputXeLL::SetGeneratedFramesCount(xell_input_handle_t context, uint32_t param1, uint32_t framesCount)
xell_result_t InputXeLL::SetGeneratedFramesCount(xell_input_handle_t context, uint32_t frameId, uint32_t framesCount)
{
// TODO: figure out params and impl
// TODO: store framesCount for the SleepParams::fg_multiplier
@@ -325,10 +325,10 @@ xell_result_t InputXeLL::SetGeneratedFramesCount(xell_input_handle_t context, ui
if (!context)
return XELL_RESULT_ERROR_INVALID_CONTEXT;
context->setGeneratedFramesCountParam1 = param1;
context->setGeneratedFramesCountFrameId = frameId;
context->framesCount = framesCount;
return InputCommon::pass_xellSetGeneratedFramesCount(context->inputContext, param1, framesCount);
return InputCommon::pass_xellSetGeneratedFramesCount(context->inputContext, frameId, framesCount);
}
#ifdef LOW_LATENCY_INPUTS
@@ -391,10 +391,10 @@ XELL_EXPORT xell_result_t xellSetFgEnabled(xell_context_handle_t context, uint32
return InputXeLL::SetFgEnabled((InputXeLL::xell_input_handle_t) context, param1, param2);
}
XELL_EXPORT xell_result_t xellSetGeneratedFramesCount(xell_context_handle_t context, uint32_t param1,
XELL_EXPORT xell_result_t xellSetGeneratedFramesCount(xell_context_handle_t context, uint32_t frameId,
uint32_t framesCount)
{
return InputXeLL::SetGeneratedFramesCount((InputXeLL::xell_input_handle_t) context, param1, framesCount);
return InputXeLL::SetGeneratedFramesCount((InputXeLL::xell_input_handle_t) context, frameId, framesCount);
}
XELL_EXPORT xell_result_t xellGetLastPresentStartFrameId(xell_context_handle_t context, uint32_t* p_frame_id)
+2 -2
View File
@@ -22,7 +22,7 @@ class InputXeLL
// Unsure what to do with those, just store for now
uint32_t setFgEnabledParam1 {};
uint32_t setFgEnabledParam2 {};
uint32_t setGeneratedFramesCountParam1 {};
uint32_t setGeneratedFramesCountFrameId {};
uint32_t framesCount {};
};
@@ -69,7 +69,7 @@ extern "C"
XELL_EXPORT xell_result_t xellD3D12SetAppQueue(xell_context_handle_t context, ID3D12CommandQueue* appQueue);
XELL_EXPORT xell_result_t xellSetDisplayInfo(xell_context_handle_t context, void* displayInfo);
XELL_EXPORT xell_result_t xellSetFgEnabled(xell_context_handle_t context, uint32_t param1, uint32_t param2);
XELL_EXPORT xell_result_t xellSetGeneratedFramesCount(xell_context_handle_t context, uint32_t param1,
XELL_EXPORT xell_result_t xellSetGeneratedFramesCount(xell_context_handle_t context, uint32_t frameId,
uint32_t framesCount);
XELL_EXPORT xell_result_t xellGetLastPresentStartFrameId(xell_context_handle_t context, uint32_t* p_frame_id);
}
@@ -103,37 +103,8 @@ bool AntiLag2::init(IUnknown* pDevice)
return false;
}
// Only DX12 is supported
bool AntiLag2::init_using_ctx(void* context)
{
if (!context)
{
LOG_ERROR("FSR Anti-Lag 2.0 init_using_ctx called with null context");
return false;
}
// TODO: try to distinguish between DX11 and DX12 contexts
dx12_ctx = *reinterpret_cast<AMD::AntiLag2DX12::Context*>(context);
if (dx12_ctx.m_pAntiLagAPI)
{
inited_using_context = true;
LOG_INFO("FSR Anti-Lag 2.0 DX12 initialized using existing context");
return true;
}
return false;
}
void AntiLag2::deinit()
{
if (inited_using_context)
{
LOG_INFO("FSR Anti-Lag 2.0 DX12 deinit called while inited using context, skipping deinitialization");
inited_using_context = false;
return;
}
{
std::scoped_lock lock(amdxc64_load_mutex);
@@ -27,7 +27,6 @@ class AntiLag2 : public LowLatencyTech
// From LowLatencyTech
bool init(IUnknown* pDevice) override;
bool init_using_ctx(void* context) override;
void deinit() override;
LowLatencyMode get_mode() override { return LowLatencyMode::AntiLag2; };
@@ -4,14 +4,6 @@
bool AntiLagVk::init(IUnknown* pDevice) { return VulkanHooks::o_vkAntiLagUpdateAMD != nullptr; }
// Unsupported
bool AntiLagVk::init_using_ctx(void* context)
{
LOG_ERROR("AntiLagVk init_using_ctx is not supported");
inited_using_context = false;
return false;
}
void* AntiLagVk::get_tech_context() { return nullptr; }
void AntiLagVk::get_sleep_status(SleepParams* sleep_params)
@@ -12,7 +12,6 @@ class AntiLagVk : public LowLatencyTech
// From LowLatencyTech
bool init(IUnknown* pDevice) override;
bool init_using_ctx(void* context) override;
void deinit() override {}; // Not used by AntiLag VK
LowLatencyMode get_mode() override { return LowLatencyMode::AntiLagVk; };
@@ -111,14 +111,6 @@ bool LatencyFlex::init(IUnknown* pDevice)
return false;
};
// Unsupported
bool LatencyFlex::init_using_ctx(void* context)
{
LOG_ERROR("LatencyFleX init_using_ctx is not supported");
inited_using_context = false;
return false;
}
void LatencyFlex::deinit()
{
deinit_mutex.lock();
@@ -31,7 +31,6 @@ class LatencyFlex : public LowLatencyTech
// From LowLatencyTech
bool init(IUnknown* pDevice) override;
bool init_using_ctx(void* context) override;
void deinit() override;
LowLatencyMode get_mode() override { return LowLatencyMode::LatencyFlex; };
@@ -10,7 +10,7 @@ void XeLL::xell_sleep(uint32_t frame_id)
sent_sleep_frame_ids[frame_id % 64] = true;
// Don't call XeLL when trying to disable XeLL with XeFG active
if (!inited_using_context || is_enabled())
if (!forced_mode || is_enabled())
{
LOG_TRACE_LOWLATENCY("Sleeping with frame_id: {}", frame_id);
o_xellSleep(xell_context, frame_id);
@@ -26,7 +26,7 @@ void XeLL::add_marker(uint32_t frame_id, xell_latency_marker_type_t marker)
return;
}
if (!inited_using_context || is_enabled())
if (!forced_mode || is_enabled())
o_xellAddMarkerData(xell_context, frame_id, marker);
}
@@ -85,7 +85,7 @@ bool XeLL::init(IUnknown* pDevice)
if (resendResult != XELL_RESULT_SUCCESS)
break;
resendResult = xellSetGeneratedFramesCount(xellInputContext->setGeneratedFramesCountParam1,
resendResult = xellSetGeneratedFramesCount(xellInputContext->setGeneratedFramesCountFrameId,
xellInputContext->framesCount);
} while (false);
@@ -99,52 +99,12 @@ bool XeLL::init(IUnknown* pDevice)
return result;
}
bool XeLL::init_using_ctx(void* context)
{
if (!XeLLProxy::InitXeLL())
{
LOG_ERROR("XeLL init_using_ctx failed to load libxell.dll");
return false;
}
if (!context)
{
LOG_ERROR("XeLL handed over to fakenvapi but the context is null");
return false;
}
if (xell_context)
{
if (o_xellDestroyContext(xell_context) == XELL_RESULT_SUCCESS)
LOG_INFO("destroy o_xell_ctx");
else
LOG_ERROR("destroy o_xell_ctx error");
}
xell_context = (xell_context_handle_t) context;
// Context is handled and held inside XeLLProxy
inited_using_context = true;
XellHooks::blockExternalContexts(true);
LOG_INFO("XeLL initialized using existing context: {:X}", (uint64_t) xell_context);
return true;
}
void XeLL::deinit()
{
XellHooks::blockExternalContexts(false);
if (inited_using_context)
{
// Let XeFG handle the context as XeLL can't be destroyed before XeFG
LOG_INFO("XeLL deinit called while inited using context, skipping deinitialization");
inited_using_context = false;
}
else
{
o_xellDestroyContext(xell_context);
LOG_INFO("XeLL deinitialized");
}
o_xellDestroyContext(xell_context);
LOG_INFO("XeLL deinitialized");
}
void* XeLL::get_tech_context() { return xell_context; }
@@ -166,7 +126,7 @@ void XeLL::set_sleep_mode(SleepMode* sleep_mode)
low_latency_enabled = sleep_mode->low_latency_enabled;
// Always report XeLL as enabled when XeFG is enabled
if (inited_using_context)
if (forced_mode)
xell_sleep_params.bLowLatencyMode = true;
else
xell_sleep_params.bLowLatencyMode = is_enabled();
@@ -304,12 +264,12 @@ xell_result_t XeLL::xellSetFgEnabled(uint32_t param1, uint32_t param2) const
return o_xellSetFgEnabled(xell_context, param1, param2);
}
xell_result_t XeLL::xellSetGeneratedFramesCount(uint32_t param1, uint32_t framesCount) const
xell_result_t XeLL::xellSetGeneratedFramesCount(uint32_t frameId, uint32_t framesCount) const
{
if (!o_xellSetGeneratedFramesCount)
return XELL_RESULT_ERROR_UNKNOWN;
return o_xellSetGeneratedFramesCount(xell_context, param1, framesCount);
return o_xellSetGeneratedFramesCount(xell_context, frameId, framesCount);
}
xell_result_t XeLL::xellGetLastPresentStartFrameId(uint32_t* p_frame_id) const
@@ -70,7 +70,6 @@ class XeLL : public LowLatencyTech
// From LowLatencyTech
bool init(IUnknown* pDevice) override;
bool init_using_ctx(void* context) override;
void deinit() override;
LowLatencyMode get_mode() override { return LowLatencyMode::XeLL; };
@@ -98,7 +97,7 @@ class XeLL : public LowLatencyTech
xell_result_t xellD3D12SetAppQueue(ID3D12CommandQueue* appQueue) const;
xell_result_t xellSetDisplayInfo(void* displayInfo) const;
xell_result_t xellSetFgEnabled(uint32_t param1, uint32_t param2) const;
xell_result_t xellSetGeneratedFramesCount(uint32_t param1, uint32_t framesCount) const;
xell_result_t xellSetGeneratedFramesCount(uint32_t frameId, uint32_t framesCount) const;
xell_result_t xellGetLastPresentStartFrameId(uint32_t* p_frame_id) const;
xell_result_t xellGetFramesReports(xell_frame_report_t* outdata) const;
};
@@ -67,18 +67,17 @@ class LowLatencyTech
ForceReflex low_latency_override = ForceReflex::InGame;
bool low_latency_enabled = false;
bool effective_fg_state = false;
bool inited_using_context = false;
bool forced_mode = false;
public:
LowLatencyTech()
: current_call_spot(CallSpot::SimulationStart), low_latency_override(ForceReflex::InGame),
low_latency_enabled(false), effective_fg_state(false), inited_using_context(false)
low_latency_enabled(false), effective_fg_state(false), forced_mode(false)
{
}
virtual ~LowLatencyTech() {}
virtual bool init(IUnknown* pDevice) = 0;
virtual bool init_using_ctx(void* context) = 0;
virtual void deinit() = 0;
virtual LowLatencyMode get_mode() = 0;
@@ -86,6 +85,7 @@ class LowLatencyTech
virtual void set_fg_type(bool interpolated, uint64_t frame_id) = 0;
virtual void set_low_latency_override(ForceReflex low_latency_override) = 0;
virtual void set_effective_fg_state(bool effective_fg_state) = 0;
virtual void set_forced_mode(bool forced_mode) { this->forced_mode = forced_mode; };
virtual bool is_enabled() = 0;
+10 -2
View File
@@ -216,7 +216,7 @@ bool fakenvapi::updateModeAndContext()
return result;
}
bool fakenvapi::setModeAndContext(void* context, LowLatencyMode mode)
bool fakenvapi::forceMode(IUnknown* device, LowLatencyMode mode)
{
LOG_FUNC();
@@ -228,7 +228,7 @@ bool fakenvapi::setModeAndContext(void* context, LowLatencyMode mode)
return false;
}
auto result = lowLatencyCtx->set_low_latency_tech_context(context, mode);
auto result = lowLatencyCtx->set_low_latency_tech_mode(device, mode);
if (!result)
LOG_TRACE_FAKENVAPI("Can't set Low Latency context from fakenvapi");
@@ -259,6 +259,14 @@ LowLatencyMode fakenvapi::getCurrentMode()
return LowLatencyMode::None;
}
void* fakenvapi::getCurrentContext()
{
if (updateModeAndContext())
return _lowLatencyTechContext;
else
return nullptr;
}
bool fakenvapi::isUsingAsMainNvapi() { return _usingFakenvapiAsMainNvapi; }
void fakenvapi::setUsingAsMainNvapi(bool usingAsMain) { _usingFakenvapiAsMainNvapi = usingAsMain; }
+3 -1
View File
@@ -39,9 +39,11 @@ class fakenvapi
static void deinit();
static void* queryInterface(NvU32 id);
static void reportFGPresent(IDXGISwapChain* pSwapChain, bool fg_state, bool frame_interpolated);
static bool setModeAndContext(void* context, LowLatencyMode mode);
static bool forceMode(IUnknown* device, LowLatencyMode mode);
static bool isLowLatencyActive();
static LowLatencyMode getCurrentMode();
static void* getCurrentContext();
static bool isUsingAsMainNvapi();
static void setUsingAsMainNvapi(bool usingAsMain);
+16 -18
View File
@@ -71,29 +71,27 @@ bool LowLatency::get_low_latency_tech_context(void** low_latency_context, LowLat
return true;
}
bool LowLatency::set_low_latency_tech_context(void* low_latency_context, LowLatencyMode low_latency_tech)
bool LowLatency::set_low_latency_tech_mode(IUnknown* device, LowLatencyMode low_latency_tech)
{
forced_low_latency_context = low_latency_context;
forced_low_latency_tech = low_latency_tech;
if (auto current_tech = currently_active_tech.load(); current_tech)
{
// If trying to init low latency tech with the same context...
if (current_tech->get_tech_context() == low_latency_context && current_tech->get_mode() == low_latency_tech)
{
// Just init itself with the correct method using that context to set the flag
current_tech->init_using_ctx(current_tech->get_tech_context());
return true;
}
}
deinit_current_tech();
// Only D3D
if (forced_low_latency_context)
return update_low_latency_tech((IUnknown*) nullptr);
else
return true; // no device, low latency will need to reinit itself on the next reflex call
if (!device)
return false;
auto result = update_low_latency_tech(device);
if (result)
{
void* lowLatencyContext = nullptr;
LowLatencyMode currentLowLatencyMode {};
return get_low_latency_tech_context(&lowLatencyContext, &currentLowLatencyMode) &&
low_latency_tech == currentLowLatencyMode;
}
return false;
}
bool LowLatency::is_low_latency_enabled()
+2 -3
View File
@@ -19,8 +19,7 @@ class LowLatency
SleepMode last_sleep_mode {};
// Once set, it will be used for all init attempts
void* forced_low_latency_context = nullptr;
LowLatencyMode forced_low_latency_tech = LowLatencyMode::LatencyFlex;
LowLatencyMode forced_low_latency_tech = LowLatencyMode::None;
void update_effective_fg_state();
void update_enabled_override();
@@ -50,7 +49,7 @@ class LowLatency
}
}
bool get_low_latency_tech_context(void** low_latency_tech_context, LowLatencyMode* low_latency_tech);
bool set_low_latency_tech_context(void* low_latency_tech_context, LowLatencyMode low_latency_tech);
bool set_low_latency_tech_mode(IUnknown* device, LowLatencyMode low_latency_tech);
bool is_low_latency_enabled();
+17 -16
View File
@@ -13,7 +13,7 @@
// private
bool LowLatency::update_low_latency_tech(IUnknown* pDevice)
{
if (!pDevice && !forced_low_latency_context)
if (!pDevice)
{
LOG_ERROR("Invalid pointer");
return false;
@@ -21,23 +21,20 @@ bool LowLatency::update_low_latency_tech(IUnknown* pDevice)
if (!currently_active_tech.load())
{
if (forced_low_latency_context && forced_low_latency_tech == LowLatencyMode::AntiLag2)
if (forced_low_latency_tech == LowLatencyMode::XeLL)
{
auto new_tech = std::make_shared<AntiLag2>();
if (new_tech->init_using_ctx(forced_low_latency_context))
auto new_tech_xell = std::make_shared<XeLL>();
if (new_tech_xell->init(pDevice))
{
LOG_INFO("LowLatency algo: FSR Anti-Lag 2.0 (via context)");
currently_active_tech.store(std::move(new_tech));
return true;
}
}
else if (forced_low_latency_context && forced_low_latency_tech == LowLatencyMode::XeLL)
{
auto new_tech = std::make_shared<XeLL>();
if (new_tech->init_using_ctx(forced_low_latency_context))
{
LOG_INFO("LowLatency algo: XeLL (via context)");
currently_active_tech.store(std::move(new_tech));
LOG_INFO("LowLatency algo: XeLL (forced)");
new_tech_xell->set_forced_mode(true);
SleepMode sleepMode {};
sleepMode.low_latency_enabled = true;
new_tech_xell->set_sleep_mode(&sleepMode);
currently_active_tech.store(std::move(new_tech_xell));
return true;
}
}
@@ -81,6 +78,10 @@ bool LowLatency::update_low_latency_tech(IUnknown* pDevice)
bool change_detected = last_force_latencyflex != force_latencyflex;
last_force_latencyflex = force_latencyflex;
// Don't allow for deinit with forced mode
if (forced_low_latency_tech != LowLatencyMode::None)
return true;
if (State::Instance().fakenvapiReloadLowLatency)
{
change_detected = true;
-73
View File
@@ -360,77 +360,4 @@ class XeLLProxy
static PFN_xellGetFramesReports GetFramesReports() { return _xellGetFramesReports; }
static PFN_xellD3D12CreateContext D3D12CreateContext() { return _xellD3D12CreateContext; }
#endif
static bool DestroyXeLLContext()
{
LOG_DEBUG("");
if (_xellContext != nullptr)
{
auto context = _xellContext;
_xellContext = nullptr;
auto xellResult = DestroyContext()(context);
LOG_INFO("XeLL DestroyContext result: {} ({})", magic_enum::enum_name(xellResult), (UINT) xellResult);
// Set it back because context is not destroyed
if (xellResult != XELL_RESULT_SUCCESS)
_xellContext = context;
}
return true;
}
static bool CreateContext(ID3D12Device* device)
{
if (!InitXeLL())
{
LOG_ERROR("XeLL proxy can't find libxell.dll!");
return false;
}
#ifndef LOW_LATENCY_INPUTS
if (!XellHooks::Hook())
{
LOG_ERROR("Couldn't detour XeLL");
return false;
}
#endif
if (_xellContext != nullptr)
DestroyXeLLContext();
xell_result_t xellResult;
{
#ifndef DONT_USE_XMX
ScopedSkipSpoofing skipSpoofing {};
#endif // !DONT_USE_XMX
xellResult = D3D12CreateContext()(device, &_xellContext);
}
if (xellResult != XELL_RESULT_SUCCESS)
{
LOG_ERROR("XeLL D3D12CreateContext error: {} ({})", magic_enum::enum_name(xellResult), (UINT) xellResult);
return false;
}
else
{
#ifdef LOW_LATENCY_INPUTS
((InputXeLL::xell_input_handle_t) _xellContext)->inputContext.localContext = true;
#endif
LOG_INFO("XeLL context created");
XellHooks::setOurContext(_xellContext);
}
xellResult = SetLoggingCallback()(_xellContext, XELL_LOGGING_LEVEL_DEBUG, xellLogCallback);
if (xellResult != XELL_RESULT_SUCCESS)
{
LOG_ERROR("XeLL SetLoggingCallback error: {} ({})", magic_enum::enum_name(xellResult), (UINT) xellResult);
}
return true;
}
static xell_context_handle_t Context() { return _xellContext; }
};