Skip to content

Commit 514a224

Browse files
committed
fix(ngx): isolate delegated initialization across threads
1 parent 946462b commit 514a224

8 files changed

Lines changed: 76 additions & 8 deletions

File tree

‎.github/workflows/just_build_no_signature.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ jobs:
133133
& $env:ComSpec /d /c $command
134134
if ($LASTEXITCODE) { throw "Compatibility guard test failed: $name" }
135135
}
136-
foreach ($script in @('test_shutdown_routing.py','test_provider_publication_routes.py','test_provider_dll_boundary.py','test_combo_release.py','test_native_operation_routes.py','test_native_module_publication.py')) {
136+
foreach ($script in @('test_shutdown_routing.py','test_provider_publication_routes.py','test_provider_dll_boundary.py','test_combo_release.py','test_native_operation_routes.py','test_native_module_publication.py','test_native_init_scopes.py')) {
137137
$command = 'call "' + $vcvars + '" && python tests\' + $script + ' --compiler cl'
138138
& $env:ComSpec /d /c $command
139139
if ($LASTEXITCODE) { throw "Compatibility routing test failed: $script" }

‎OptiScaler/State.h‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,6 @@ class State
180180
std::wstring NVNGX_ApplicationDataPath;
181181
std::string NVNGX_ProjectId;
182182
NVSDK_NGX_Version NVNGX_Version {};
183-
const NVSDK_NGX_FeatureCommonInfo* NVNGX_FeatureInfo = nullptr;
184183
std::vector<std::wstring> NVNGX_FeatureInfo_Paths;
185184
NVSDK_NGX_LoggingInfo NVNGX_Logger { nullptr, NVSDK_NGX_LOGGING_LEVEL_OFF, false };
186185
NVSDK_NGX_EngineType NVNGX_Engine = NVSDK_NGX_ENGINE_TYPE_CUSTOM;

‎OptiScaler/inputs/NVNGX_DLSS_Dx11.cpp‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ static ID3D11Device* D3D11Device = nullptr;
2121
static ankerl::unordered_dense::map<unsigned int, ContextData<IFeature_Dx11>> Dx11Contexts;
2222
static int evalCounter = 0;
2323
static bool shutdown = false;
24-
static bool _skipInit = false;
24+
static thread_local bool _skipInit = false;
2525
static wchar_t const** paths;
2626

2727
class ScopedInitDx11
@@ -153,7 +153,6 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D11_Init_Ext(unsigned long long InApp
153153
State::Instance().NVNGX_ApplicationId = InApplicationId;
154154
State::Instance().NVNGX_ApplicationDataPath = std::wstring(InApplicationDataPath);
155155
State::Instance().NVNGX_Version = InSDKVersion;
156-
State::Instance().NVNGX_FeatureInfo = InFeatureInfo;
157156
State::Instance().NVNGX_Version = InSDKVersion;
158157

159158
if (Config::Instance()->DLSSEnabled.value_or_default() && !_skipInit)

‎OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ static std::unordered_map<unsigned int, NVSDK_NGX_Feature> HandleToFeature;
3333
static ID3D12Device* D3D12Device = nullptr;
3434
static int evalCounter = 0;
3535
static bool shutdown = false;
36-
static bool _skipInit = false;
36+
static thread_local bool _skipInit = false;
3737
static wchar_t const** paths;
3838

3939
class ScopedInitDx12
@@ -167,7 +167,6 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_Init_Ext(unsigned long long InApp
167167
State::Instance().NVNGX_ApplicationId = InApplicationId;
168168
State::Instance().NVNGX_ApplicationDataPath = std::wstring(InApplicationDataPath);
169169
State::Instance().NVNGX_Version = InSDKVersion;
170-
State::Instance().NVNGX_FeatureInfo = &localFeatureInfo;
171170
State::Instance().NVNGX_Version = InSDKVersion;
172171

173172
if (Config::Instance()->DLSSEnabled.value_or_default() && !_skipInit)

‎OptiScaler/inputs/NVNGX_DLSS_Vk.cpp‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ PFN_vkGetDeviceProcAddr vkGDPA;
2727
static ankerl::unordered_dense::map<unsigned int, ContextData<IFeature_Vk>> VkContexts;
2828
static int evalCounter = 0;
2929
static bool shutdown = false;
30-
static bool _skipInit = false;
30+
static thread_local bool _skipInit = false;
3131
static wchar_t const** paths;
3232

3333
class ScopedInitVk
@@ -163,7 +163,6 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_VULKAN_Init_Ext2(
163163
State::Instance().NVNGX_ApplicationId = InApplicationId;
164164
State::Instance().NVNGX_ApplicationDataPath = std::wstring(InApplicationDataPath);
165165
State::Instance().NVNGX_Version = InSDKVersion;
166-
State::Instance().NVNGX_FeatureInfo = &localFeatureInfo;
167166
State::Instance().NVNGX_Version = InSDKVersion;
168167

169168
if (Config::Instance()->DLSSEnabled.value_or_default() && !_skipInit)

‎docs/CODEX_HANDOFF.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,5 @@ Latest validated code checkpoint: e737b087 (Windows build 35129887577 and format
4040
The next checkpoint `3df84993` fixes native D3D12/Vulkan device-specific Init/Shutdown and removes the nested DLSSFeatureDx12 native shutdown. Windows build `35188944901` and format `35188944895` succeeded; 37 device and 43 extracted shutdown checks passed. See [native lifecycle audit](NATIVE_DEVICE_LIFECYCLE_20260917.md), including the operation-admission follow-up, test counts and exact remaining boundaries. Do not restart this ownership work or rerun unrelated installer suites. The latest user explicitly permits aurora synchronization after code/review/build completion without waiting for game tests; remaining lifecycle boundaries must still be resolved or explicitly scoped before that gate is met.
4141

4242
Checkpoint `914c2518` adds native operation admission, cached-call-pointer guards and correct parameter-destroy failure reporting. Windows build `35189573586` and format `35189573598` succeeded (55 device, 43 shutdown, 236 operation checks). The subsequent native-module publication fix uses a private immutable export table; read [its audit](NATIVE_MODULE_PUBLICATION_20260917.md) rather than reimplementing it. Shared Init path/state metadata and replacement-provider shutdown/handle generations remain unfinished; real-game validation is deferred by the user's request, and aurora synchronization has not yet been performed.
43+
44+
Native module publication is committed in `33ef3e0a` with incremental formatting follow-up `946462b0`. The next scope fix makes delegated-Init skip flags thread-local in D3D11/D3D12/Vulkan and removes unused dangling State::NVNGX_FeatureInfo writes; 24 extracted scope checks pass. Both are detailed in the same module-publication audit. Do not equate these bounded fixes with full State/Config synchronization.

‎docs/NATIVE_MODULE_PUBLICATION_20260917.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,11 @@ The existing explicit-module preference, package/override/driver search order, h
1313
All seven scenarios passed locally (776 assertions, including repeated export-table checks). The affected 43 shutdown and 236 operation routing checks also passed after adapting their test-owned table accessor. The new scenario script is included in Windows build CI. Full cumulative build results will be added when available.
1414

1515
This validates publication ordering, not the real loader, Detours, driver or GPU. InitNVNGX's existing void interface still expresses unavailable/pending through null accessors, not a new public status. Outer initialization fallback policies remain unchanged. Shared initialization metadata/path vectors, D3D11 device ownership, native stale-handle generations, parameter query admission and replacement-provider shutdown remain separate audit items. Do not treat this checkpoint as completion of all global concurrency work or proof of game crash resolution.
16+
17+
## Delegated Init scope follow-up
18+
19+
D3D11, D3D12 and Vulkan each used a global `_skipInit` flag while forwarding an Init variant to another adapter. A second thread could observe that flag and skip its own native Init/path setup. These three delegation flags are now thread-local. Same-thread forwarding/nesting still restores the previous flag, including exception unwind; no new blocking lock is introduced.
20+
21+
Removed the unused `State::NVNGX_FeatureInfo` pointer and its three writes. The D3D12/Vulkan assignments stored the address of a stack-local common-info structure. Repository-wide reference inspection found no reader, so removal changes no intended behavior and prevents future code from consuming a dangling pointer. This removal is not claimed as proof of an observed crash cause.
22+
23+
`test_native_init_scopes.py` compiles each actual scope class and flag declaration. Its 24 checks passed locally, covering nested same-thread forwarding, isolation of a second thread, previous-state restoration and exception unwind for all three APIs. This does not make shared State strings/path vectors thread-safe, nor distinguish unrelated same-thread native reentry from the existing intentional delegation scope.

‎tests/test_native_init_scopes.py‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"""Validate actual delegated-Init skip scopes across threads and exception unwind."""
2+
from pathlib import Path
3+
import argparse
4+
import re
5+
import subprocess
6+
import tempfile
7+
from test_shutdown_routing import function
8+
9+
ROOT=Path(__file__).resolve().parents[1]
10+
PRELUDE=r'''
11+
#include <future>
12+
#include <chrono>
13+
#include <stdexcept>
14+
#include <cstdlib>
15+
#include <iostream>
16+
int checks=0;
17+
void check(bool ok) { if(!ok) { std::cerr << "Init scope check " << checks+1 << " failed\n"; std::exit(2); } ++checks; }
18+
'''
19+
20+
def main():
21+
parser=argparse.ArgumentParser(); parser.add_argument('--compiler',required=True); parser.add_argument('--driver')
22+
args=parser.parse_args(); cpp=PRELUDE; checks=[]
23+
for api in ('Dx11','Dx12','Vk'):
24+
source=(ROOT/f'OptiScaler/inputs/NVNGX_DLSS_{api}.cpp').read_text(encoding='utf-8')
25+
flag=re.search(r'static (?:thread_local )?bool _skipInit = false;',source).group(0)
26+
scope=function(source,'class ScopedInit'+api)+';'
27+
cpp+='\nnamespace '+api+' {\n'+flag+'\n'+scope+'\n}\n'
28+
checks.append(f'''
29+
{{
30+
using namespace {api};
31+
check(!_skipInit);
32+
{{
33+
ScopedInit{api} outer;
34+
check(_skipInit);
35+
{{ ScopedInit{api} inner; check(_skipInit); }}
36+
check(_skipInit);
37+
auto other=std::async(std::launch::async, [] {{
38+
if(_skipInit) return false;
39+
{{ ScopedInit{api} worker; if(!_skipInit) return false;
40+
{{ ScopedInit{api} nested; if(!_skipInit) return false; }}
41+
if(!_skipInit) return false;
42+
}}
43+
return !_skipInit;
44+
}});
45+
check(other.wait_for(std::chrono::seconds(5))==std::future_status::ready && other.get());
46+
check(_skipInit);
47+
}}
48+
check(!_skipInit);
49+
try {{ ScopedInit{api} guard; throw std::runtime_error("init"); }} catch(const std::runtime_error&) {{}}
50+
check(!_skipInit);
51+
}}
52+
''')
53+
cpp+='int main() {\n'+ '\n'.join(checks)+'\nstd::cout << "PASS: " << checks << " native Init scope checks\\n";\n}\n'
54+
with tempfile.TemporaryDirectory(prefix='aurora-init-scopes-') as directory:
55+
path=Path(directory); test=path/'test.cpp'; exe=path/'test.exe'; test.write_text(cpp,encoding='utf-8')
56+
command=[args.compiler]+([args.driver] if args.driver else [])
57+
if Path(args.compiler).stem.lower()=='cl': command+=['/nologo','/EHsc','/std:c++20',str(test),'/Fe:'+str(exe)]
58+
else: command+=['-std=c++20',str(test),'-o',str(exe)]
59+
subprocess.run(command,cwd=path,check=True)
60+
subprocess.run([str(exe)],cwd=path,check=True,timeout=30)
61+
62+
if __name__=='__main__': main()

0 commit comments

Comments
 (0)