Skip to content

Commit b5f5ad6

Browse files
committed
fix: adopt upstream XeLL module routing and Vulkan menu guards
1 parent b9e9feb commit b5f5ad6

7 files changed

Lines changed: 176 additions & 29 deletions

File tree

‎.github/workflows/just_build_no_signature.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ jobs:
144144
& $env:ComSpec /d /c $command
145145
if ($LASTEXITCODE) { throw "Compatibility guard test failed: $name" }
146146
}
147-
foreach ($script in @('test_october_compatibility.py','test_vulkan_timing_results.py','test_depth_copy_resource.py','test_hud_publication.py','test_hud_copy_failure.py','test_shutdown_routing.py','test_provider_publication_routes.py','test_provider_dll_boundary.py','test_combo_release.py','test_combo_shutdown.py','test_native_operation_routes.py','test_native_module_publication.py','test_native_init_scopes.py', 'test_exported_init_chains.py','test_ngx_init_paths.py','test_ngx_init_metadata.py','test_provider_release_admission.py','test_provider_call_admission.py','test_provider_init_footprint.py','test_provider_handle_api.py','test_upstream_resource_guards.py','test_provider_settings.py','test_provider_shutdown_drain.py','test_ffx_release.py','test_ffx_create.py','test_combo_create.py','test_provider_init_allocation.py')) {
147+
foreach ($script in @('test_xell_module_routing.py','test_october_compatibility.py','test_vulkan_timing_results.py','test_depth_copy_resource.py','test_hud_publication.py','test_hud_copy_failure.py','test_shutdown_routing.py','test_provider_publication_routes.py','test_provider_dll_boundary.py','test_combo_release.py','test_combo_shutdown.py','test_native_operation_routes.py','test_native_module_publication.py','test_native_init_scopes.py', 'test_exported_init_chains.py','test_ngx_init_paths.py','test_ngx_init_metadata.py','test_provider_release_admission.py','test_provider_call_admission.py','test_provider_init_footprint.py','test_provider_handle_api.py','test_upstream_resource_guards.py','test_provider_settings.py','test_provider_shutdown_drain.py','test_ffx_release.py','test_ffx_create.py','test_combo_create.py','test_provider_init_allocation.py')) {
148148
$command = 'call "' + $vcvars + '" && python tests\' + $script + ' --compiler cl'
149149
& $env:ComSpec /d /c $command
150150
if ($LASTEXITCODE) { throw "Compatibility routing test failed: $script" }

‎OptiScaler/hooks/Kernel_Hooks.cpp‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <misc/IdentifyGpu.h>
1616

1717
#include "Hook_Utils.h"
18+
#include <proxies/XeLL_Proxy.h>
1819

1920
#include "Amdxc64_Hooks.h"
2021
#pragma intrinsic(_ReturnAddress)
@@ -194,8 +195,17 @@ BOOL WINAPI KernelHooks::hk_K32_GetModuleHandleExA(DWORD dwFlags, LPCSTR lpModul
194195
{
195196
if (lpModuleName && dwFlags == 0 && strcmp("libxell.dll", lpModuleName) == 0 && phModule)
196197
{
198+
#ifdef LOW_LATENCY_INPUTS
197199
*phModule = dllModule;
198200
return true;
201+
#else
202+
if (const auto module = XeLLProxy::Module())
203+
{
204+
// Acquire the OS reference for the selected module, not another same-named DLL.
205+
return o_K32_GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast<LPCSTR>(module),
206+
phModule);
207+
}
208+
#endif
199209
}
200210

201211
return o_K32_GetModuleHandleExA(dwFlags, lpModuleName, phModule);

‎OptiScaler/hooks/Kernel_Hooks.h‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,8 @@ class KernelHooks
8484
if (o_K32_GetModuleHandleW == nullptr)
8585
o_K32_GetModuleHandleW = Kernel32Proxy::Hook_GetModuleHandleW(hk_K32_GetModuleHandleW);
8686

87-
#ifdef LOW_LATENCY_INPUTS
8887
if (o_K32_GetModuleHandleExA == nullptr)
8988
o_K32_GetModuleHandleExA = Kernel32Proxy::Hook_GetModuleHandleExA(hk_K32_GetModuleHandleExA);
90-
#endif
9189

9290
if (o_K32_GetModuleHandleExW == nullptr)
9391
o_K32_GetModuleHandleExW = Kernel32Proxy::Hook_GetModuleHandleExW(hk_K32_GetModuleHandleExW);

‎OptiScaler/menu/menu_common.cpp‎

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3407,10 +3407,12 @@ void MenuCommon::RenderFrameGenerationSelection(RenderMenuContext& ctx)
34073407
nvngxOptions[fgNvngxArtursIndex].set_disabled(!state.artursFgFileAvailable, AuroraUtf8(L"缺少 dlss-enabler-headless.dll"));
34083408

34093409
auto constexpr fgNvngxFfxIndex = (uint32_t) FGNvngxReplacement::FFX;
3410+
nvngxOptions[fgNvngxFfxIndex].set_disabled(state.swapchainApi == API::Vulkan, AuroraUtf8(L"当前 API 不支持"));
34103411
nvngxOptions[fgNvngxFfxIndex].set_disabled(!FfxApiProxy::IsFGReady(false),
34113412
AuroraUtf8(L"缺少 amd_fidelityfx_framegeneration_dx12.dll"));
34123413

34133414
auto constexpr fgNvngxComboIndex = (uint32_t) FGNvngxReplacement::Combo;
3415+
nvngxOptions[fgNvngxComboIndex].set_disabled(state.swapchainApi == API::Vulkan, AuroraUtf8(L"当前 API 不支持"));
34143416
nvngxOptions[fgNvngxComboIndex].set_disabled(
34153417
!FfxApiProxy::IsFGReady(false) || !state.artursFgFileAvailable,
34163418
AuroraUtf8(L"缺少 amd_fidelityfx_framegeneration_dx12.dll\n或缺少 dlss-enabler-headless.dll"));
@@ -5266,13 +5268,16 @@ void MenuCommon::RenderFakenvapiSettings(RenderMenuContext& ctx)
52665268
auto& state = ctx.state;
52675269
auto config = ctx.config;
52685270

5269-
// FAKENVAPI ---------------------------
5270-
ImGui::SeparatorText("fakenvapi");
5271-
52725271
// Using state.reflexLimitsFps as a detection for Reflex being used on Nvidia
5273-
bool showLatencyFlex =
5272+
const bool showLatencyFlex =
52745273
fakenvapi::isUsingAsMainNvapi() || (state.activeFgOutput == FGOutput::XeFG && state.reflexLimitsFps);
52755274

5275+
const bool showForceXell = state.swapchainApi != API::Vulkan;
5276+
if (!showLatencyFlex && !showForceXell)
5277+
return;
5278+
5279+
ImGui::SeparatorText("fakenvapi");
5280+
52765281
if (showLatencyFlex)
52775282
{
52785283
ImGui::BeginDisabled(state.activeFgOutput == FGOutput::XeFG || state.activeFgInput == FGInput::ForceXeLL);
@@ -5284,25 +5289,27 @@ void MenuCommon::RenderFakenvapiSettings(RenderMenuContext& ctx)
52845289
ShowHelpMarker(AURORA_CN("默认情况下,只要可用就优先使用 FSR Anti-Lag 2.0 / XeLL。\n启用此项可强制改用 LatencyFlex。"));
52855290
ImGui::EndDisabled();
52865291

5287-
// Keep Force XeLL on the same line if LatencyFlex is visible
5288-
ImGui::SameLine(0.0f, 16.0f);
52895292
}
52905293

5291-
// Force XeLL is always visible
5292-
bool forceXell = config->ForceXeLL.value_or_default();
5293-
static bool activeForceXeLL = forceXell;
5294-
5295-
if (ImGui::Checkbox(AURORA_CN("强制 XeLL"), &forceXell))
5294+
if (showForceXell)
52965295
{
5297-
config->ForceXeLL = forceXell;
5298-
}
5299-
ShowHelpMarker(AURORA_CN("允许非 Intel 显卡在未启用 FG 时使用 XeLL。\n\n会禁用 FG 选项。\n\n需要重启游戏。"));
5296+
if (showLatencyFlex)
5297+
ImGui::SameLine(0.0f, 16.0f);
5298+
bool forceXell = config->ForceXeLL.value_or_default();
5299+
static bool activeForceXeLL = forceXell;
53005300

5301-
if (activeForceXeLL != forceXell)
5302-
{
5303-
ImGui::Spacing();
5304-
ImGui::TextColored(toneMapColor(ImVec4(1.f, 0.f, 0.0f, 1.f)), AURORA_CN("请保存 INI 并重启游戏后生效"));
5305-
ImGui::Spacing();
5301+
if (ImGui::Checkbox(AURORA_CN("强制 XeLL"), &forceXell))
5302+
{
5303+
config->ForceXeLL = forceXell;
5304+
}
5305+
ShowHelpMarker(AURORA_CN("允许非 Intel 显卡在未启用 FG 时使用 XeLL。\n\n会禁用 FG 选项。\n\n需要重启游戏。"));
5306+
5307+
if (activeForceXeLL != forceXell)
5308+
{
5309+
ImGui::Spacing();
5310+
ImGui::TextColored(toneMapColor(ImVec4(1.f, 0.f, 0.0f, 1.f)), AURORA_CN("请保存 INI 并重启游戏后生效"));
5311+
ImGui::Spacing();
5312+
}
53065313
}
53075314

53085315
if (showLatencyFlex)

‎OptiScaler/proxies/XeLL_Proxy.h‎

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -179,13 +179,6 @@ class XeLLProxy
179179

180180
if (mainModule != nullptr)
181181
{
182-
// We don't control which XeLL dll XeFG will pick
183-
// Detouring GetModuleHandleExA seemingly isn't enough
184-
#ifndef LOW_LATENCY_INPUTS
185-
if (_memoryDll && mainModule != _memoryDll)
186-
RedirectAllExports(_memoryDll, mainModule);
187-
#endif
188-
189182
break;
190183
}
191184
}

‎docs/UPSTREAM_AUDIT_20261005.md‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# 2026-10-05 增量吸收:XeLL 与 Vulkan 菜单
2+
3+
基线 Aurora `b9e9feb8`(已发布二进制为 v1.1.1 / `b7dbe773`)。官方 master 从 `d63306ea` 到 `500ed3354f61c7463e086243f6968645c2a90148` 新增四个提交。本次继续优先吸收明确、可适配的上游修复,不重审历史、不扩大安装器工作。
4+
5+
## 本批适配
6+
7+
- [3658a08f](https://github.com/optiscaler/OptiScaler/commit/3658a08f2ec7da0f3ad31ba27d3823e5ef1a08f9):非 LOW_LATENCY_INPUTS 构建也安装 GetModuleHandleExA hook;仅拦截无附加标志、有效输出指针的 `libxell.dll` 查询。已选择模块时,交由原始 Windows API 按模块地址获取引用,不把另一份同名库当作目标;未选择时继续原始查询。移除该路径的全导出重定向,保留低延迟输入构建原行为和其独立初始化路径。未增加延迟加载或模块卸载机制,不声称完整 loader 并发问题已解决。
8+
- [b49177b0](https://github.com/optiscaler/OptiScaler/commit/b49177b0d7b092bc69a94a6a2bb8becfeadd0a1c):Vulkan 禁用 FFX/Combo Nvngx replacement,隐藏强制 XeLL。保留适用的 LatencyFlex 项、中文提示和 DX11/DX12 菜单行为。此为选项可用性修复,不改现有保存配置或宣称增加 Vulkan 后端支持。
9+
10+
## 不重复搬入的内容
11+
12+
- [ef4e5706](https://github.com/optiscaler/OptiScaler/commit/ef4e57068f7d808b91a12c137608d1a93a8c0e50):给旧 D3D12 Shutdown 中 currentFeature 解引用加空指针判断。Aurora 的关闭路径此前已改造,旧嵌套 Shutdown 被移除,不恢复旧代码再加判断。
13+
- [500ed335](https://github.com/optiscaler/OptiScaler/commit/500ed3354f61c7463e086243f6968645c2a90148):D3D12 hook 状态跟踪重构,差异约两千行。涉及命令列表状态及线程录制,不作为小修复整套搬入;需要与 Aurora 资源跟踪、恢复语义逐项比较。
14+
- 官方 release/0.9 仍为 `f1dc3782`,reprojection 为 `44cfee4d`,unify-upscaler-inputs 为 `97a18c36`;PR #1183 仍为 `8abf6d7f`,相关尺寸修复已在 Aurora v1.1.1 完成。
15+
16+
## 相关仓库复核
17+
18+
以下默认分支与 10 月 4 日库存一致:Susemi `3def828d`、RTX40MFG-Unlock `706cb1b8`、原神桥接 `02f54639`、wilsjo2 新 NR 仓库 `1bd39091`、旧 NR 仓库 `97376162`、y4 `7b7220bb`、gprocunier `7233fc0c`、sm86 `9621db57`。这不表示审完全站所有 fork/分支。
19+
20+
deYangar 中文 fork 新到 `b6c9cc5c`,新增翻译空响应兜底修复、官方同步与格式工具更新。Aurora 不使用同一自动翻译流水线,不移植其 CI 改造。原神桥接、巫师3毛发子系统、XeFG 非所属交换链身份和 Streamline 加载锁仍保留上次列出的评审边界,不因暂无新提交就视作已吸收或已解决。
21+
22+
## 验证
23+
24+
`tests/test_xell_module_routing.py` 抽取生产模块查询函数,在普通模式 118 项、LOW_LATENCY_INPUTS 模式 108 项检查通过;覆盖目标/非目标模块、未初始化、Windows API 失败、标志和空指针原样转发,并执行 Vulkan 菜单条件及显示前缀。模块引用由计数替身验证,不是在 Windows loader/GPU 中运行;检查不证明所有模块生命周期安全。
25+
26+
完整 Windows DLL 构建和打包结果随后记录;通过前不把本批称为正式发布。没有真实游戏/性能测试,不承诺 FPS 增幅或巫师3、绝区零症状全部解决。

‎tests/test_xell_module_routing.py‎

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
"""Execute the production module hook in both build modes, and Vulkan menu gates.
2+
3+
Win32 calls and UI state are stand-ins: this does not establish loader/GPU safety.
4+
"""
5+
import argparse
6+
from pathlib import Path
7+
import re
8+
import subprocess
9+
import tempfile
10+
from test_shutdown_routing import function
11+
12+
ROOT = Path(__file__).resolve().parents[1]
13+
14+
15+
def main():
16+
parser = argparse.ArgumentParser()
17+
parser.add_argument('--compiler', required=True)
18+
parser.add_argument('--driver')
19+
args = parser.parse_args()
20+
hook = function((ROOT/'OptiScaler/hooks/Kernel_Hooks.cpp').read_text(encoding='utf-8'),
21+
'BOOL WINAPI KernelHooks::hk_K32_GetModuleHandleExA(')
22+
menu = (ROOT/'OptiScaler/menu/menu_common.cpp').read_text(encoding='utf-8')
23+
gates = []
24+
for key in ['Ffx', 'Combo']:
25+
gate = re.search(r'nvngxOptions\[fgNvngx'+key+r'Index\]\.set_disabled\((state.swapchainApi[^,]+),', menu)
26+
assert gate, key
27+
gates.append(gate[1])
28+
render = function(menu, 'void MenuCommon::RenderFakenvapiSettings(')
29+
start = render.index(' const bool showLatencyFlex')
30+
end = render.index(' ImGui::SeparatorText(', start)
31+
prefix = render[start:end]
32+
assert 'if (showForceXell)' in render and render.index('if (showForceXell)') < render.index('bool forceXell')
33+
# No arbitrary export patching in the non-input initialization path.
34+
xell = (ROOT/'OptiScaler/proxies/XeLL_Proxy.h').read_text(encoding='utf-8')
35+
assert 'RedirectAllExports' not in function(xell, 'static bool InitXeLLProper(')
36+
cpp = r'''
37+
#include <cstring>
38+
#include <cstdlib>
39+
#include <iostream>
40+
using BOOL=int;using DWORD=unsigned;using HMODULE=void*;using LPCSTR=const char*;
41+
#define WINAPI
42+
constexpr unsigned GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS=4;
43+
int dllStorage,selectedStorage,otherStorage;
44+
HMODULE dllModule=&dllStorage;
45+
struct XeLLProxy {static inline HMODULE selected=nullptr;static HMODULE Module(){return selected;}};
46+
int calls=0,refs=0;DWORD lastFlags;LPCSTR lastName;HMODULE* lastOut;bool succeed=true;
47+
BOOL original(DWORD flags,LPCSTR name,HMODULE* out){
48+
++calls;lastFlags=flags;lastName=name;lastOut=out;
49+
if(!out||!succeed)return 0;
50+
*out=flags==4?const_cast<char*>(name):static_cast<void*>(&otherStorage);++refs;return 1;
51+
}
52+
struct KernelHooks {static inline auto o_K32_GetModuleHandleExA=&original;
53+
static BOOL hk_K32_GetModuleHandleExA(DWORD,LPCSTR,HMODULE*);};
54+
enum class API {DX11,DX12,Vulkan};enum class FGOutput {NoFG,XeFG};
55+
struct State {API swapchainApi;FGOutput activeFgOutput;bool reflexLimitsFps;};
56+
namespace fakenvapi {bool main=false;bool isUsingAsMainNvapi(){return main;}}
57+
int section=0;bool visibleForce=false;
58+
void visibility(State state){
59+
''' + prefix + '++section;visibleForce=showForceXell;}\n' + hook
60+
for i, gate in enumerate(gates):
61+
cpp += f'bool gate{i}(State state){{return {gate};}}\n'
62+
cpp += r'''
63+
int main(){int checks=0;auto check=[&](bool v){++checks;if(!v){std::cerr<<"FAIL "<<checks;std::exit(2);}};
64+
const char* name="libxell.dll";HMODULE out=nullptr;
65+
for(bool selected:{false,true}){
66+
XeLLProxy::selected=selected?&selectedStorage:nullptr;
67+
calls=refs=0;succeed=true;out=nullptr;
68+
check(KernelHooks::hk_K32_GetModuleHandleExA(0,name,&out)==1);
69+
#ifdef LOW_LATENCY_INPUTS
70+
check(out==dllModule&&calls==0);
71+
#else
72+
check(calls==1&&refs==1);check(out==(selected?&selectedStorage:&otherStorage));
73+
check(lastFlags==(selected?4u:0u));check(lastName==(selected?reinterpret_cast<LPCSTR>(&selectedStorage):name));
74+
succeed=false;out=nullptr;calls=refs=0;
75+
check(!KernelHooks::hk_K32_GetModuleHandleExA(0,name,&out));check(calls==1&&refs==0&&out==nullptr);
76+
#endif
77+
}
78+
// Unrelated names, invalid output/name, and flagged/address requests forward untouched.
79+
succeed=true;
80+
for(DWORD flags:{1u,2u,4u,6u}){calls=0;KernelHooks::hk_K32_GetModuleHandleExA(flags,name,&out);
81+
check(calls==1&&lastFlags==flags&&lastName==name&&lastOut==&out);}
82+
for(auto other:{"other.dll","C:\\game\\libxell.dll"}){calls=0;
83+
KernelHooks::hk_K32_GetModuleHandleExA(0,other,&out);check(calls==1&&lastName==other);}
84+
calls=0;KernelHooks::hk_K32_GetModuleHandleExA(0,nullptr,&out);check(calls==1&&lastName==nullptr);
85+
calls=0;KernelHooks::hk_K32_GetModuleHandleExA(0,name,nullptr);check(calls==1&&lastOut==nullptr);
86+
for(API api:{API::DX11,API::DX12,API::Vulkan})for(bool main:{false,true})
87+
for(FGOutput fg:{FGOutput::NoFG,FGOutput::XeFG})for(bool reflex:{false,true}){
88+
State state{api,fg,reflex};fakenvapi::main=main;section=0;visibleForce=false;visibility(state);
89+
check(section==(api!=API::Vulkan||main||(fg==FGOutput::XeFG&&reflex)));
90+
check(visibleForce==(api!=API::Vulkan));
91+
check(gate0(state)==(api==API::Vulkan));check(gate1(state)==(api==API::Vulkan));
92+
}
93+
std::cout<<"PASS: "<<checks<<" XeLL/menu checks\n";}
94+
'''
95+
with tempfile.TemporaryDirectory(prefix='aurora-xell-routing-') as directory:
96+
path = Path(directory)
97+
test = path/'test.cpp'
98+
test.write_text(cpp, encoding='utf-8')
99+
for low_latency in [False, True]:
100+
exe = path/('input.exe' if low_latency else 'normal.exe')
101+
command = [args.compiler] + ([args.driver] if args.driver else [])
102+
if Path(args.compiler).stem.lower() == 'cl':
103+
command += ['/nologo','/EHsc','/std:c++20',str(test),'/Fe:'+str(exe)]
104+
if low_latency: command += ['/DLOW_LATENCY_INPUTS']
105+
else:
106+
command += ['-std=c++20',str(test),'-o',str(exe)]
107+
if low_latency: command += ['-DLOW_LATENCY_INPUTS']
108+
subprocess.run(command,cwd=path,check=True)
109+
subprocess.run([str(exe)],cwd=path,check=True,timeout=25)
110+
111+
112+
if __name__ == '__main__':
113+
main()

0 commit comments

Comments
 (0)