Skip to content

Commit 9bd9659

Browse files
committed
fix(hud): retry failed initialization without retaining partial resources
1 parent b00d8e4 commit 9bd9659

6 files changed

Lines changed: 76 additions & 8 deletions

File tree

‎OptiScaler/framegen/nvngx/Nvngx_FG.h‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,16 @@ class Nvngx_FG
4646
{
4747
if (!device)
4848
return nullptr;
49-
return _hudCopy.GetOrCreate([&] { return std::make_unique<HudCopy_Dx12>("HudCopy", device); }).provider;
49+
return _hudCopy
50+
.GetOrCreate(
51+
[&]
52+
{
53+
auto candidate = std::make_unique<HudCopy_Dx12>("HudCopy", device);
54+
if (!candidate->IsInit())
55+
candidate.reset(); // Do not permanently publish a failed optional pass.
56+
return candidate;
57+
})
58+
.provider;
5059
}
5160

5261
// Caller holds transition admission. Allocate both records before entering

‎OptiScaler/shaders/hud_copy/HudCopy_Dx12.cpp‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,19 @@ HudCopy_Dx12::HudCopy_Dx12(std::string InName, ID3D12Device* InDevice) : Shader_
176176

177177
HudCopy_Dx12::~HudCopy_Dx12()
178178
{
179-
if (!_init || State::Instance().isShuttingDown)
179+
if (State::Instance().isShuttingDown)
180180
return;
181181

182+
if (!_init)
183+
{
184+
// Construction can return after allocating only part of the shader.
185+
// No Dispatch was possible; the base destructor skips these resources
186+
// when initialization failed, so retire them here before a later retry.
187+
SAFE_RELEASE(_pipelineState);
188+
SAFE_RELEASE(_rootSignature);
189+
SAFE_RELEASE(_constantBuffer);
190+
}
191+
182192
for (int i = 0; i < HudCopy_NUM_OF_HEAPS; i++)
183193
{
184194
_frameHeaps[i].ReleaseHeaps();

‎docs/AURORA_NEXT_RELEASE_DRAFT.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,5 @@ HUD 内部缓冲与当前画面的尺寸、格式、子资源或采样参数不
7171
- 同一个 HUD 对象的 CPU Dispatch 不再允许并发或重入改写缓冲、描述符及计数状态。采用非等待保护,避免在外部回调中相互等待。
7272
- NGX 共享 HUD 对象改为完整构造后一次发布,防止首次调用时重复创建/替换;从当前命令列表查询设备,不再依赖全局当前设备。
7373

74-
这是 CPU 调用与发布保护,不表示 GPU 已完成此前提交的工作。跨设备自动重建、GPU 常量/描述符复用和资源退役仍需单独完善。
74+
这是 CPU 调用与发布保护,不表示 GPU 已完成此前提交的工作。跨设备自动重建、GPU 常量/描述符复用和资源退役仍需单独完善。
75+
- HUD initialization failures no longer permanently disable the optional HUD pass through a cached unready context; early-return partial shader allocations are reclaimed before retry. CPU ownership checks cover failed/ready context teardown and later retry; real GPU allocation-failure recovery remains unverified.

‎docs/CODEX_HANDOFF.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,4 +146,5 @@ Nvngx_FG now publishes its shared HUD object using the existing ProviderPublicat
146146
New actual helper/field extraction test uses production ProviderPublication, with constructor reentry, worker-thread construction lookup, failure/retry, immutable identity and one destructor; 8 checks pass. Affected API handle routing passes 42. Existing shared shader Dispatch checks pass 564. Immutable publication closes the NGX singleton construction race; it does not solve concurrent destruction at process teardown or queued GPU reuse. No claim of GPU completion/fence ownership. Added publication test to Windows CI.
147147
### Final device/admission/publication validation
148148

149-
2026-10-02 confirmation: 6832332f and final a9f28434 are pushed. First Windows run 36849028967 succeeded. Final run 36849297779 / job 110326885532 and format 36849297777 succeeded, including full DLL, runtime inventory, all configured compatibility checks, package/upload. MSVC confirms 564 HUD Dispatch assertions, 8 HUD publication checks and 42 API handle checks. Artifact OptiScaler_Aurora_v1.0_20261001_compat_a9f28434.7z, id 11154374282, 219573606 bytes, Actions digest sha256:a381566f2f38de5c891ddc81d61cb12ea126709bd2af2a39dcfb5dfc5993f003. Main merge and Release have not occurred. Continue GPU submission/retirement ownership and partial Init transactions; do not recreate these completed CPU/device/publication guards.
149+
2026-10-02 confirmation: 6832332f and final a9f28434 are pushed. First Windows run 36849028967 succeeded. Final run 36849297779 / job 110326885532 and format 36849297777 succeeded, including full DLL, runtime inventory, all configured compatibility checks, package/upload. MSVC confirms 564 HUD Dispatch assertions, 8 HUD publication checks and 42 API handle checks. Artifact OptiScaler_Aurora_v1.0_20261001_compat_a9f28434.7z, id 11154374282, 219573606 bytes, Actions digest sha256:a381566f2f38de5c891ddc81d61cb12ea126709bd2af2a39dcfb5dfc5993f003. Main merge and Release have not occurred. Continue GPU submission/retirement ownership and partial Init transactions; do not recreate these completed CPU/device/publication guards.
150+
2026-10-02 continuation repairs NGX HUD publication after unsuccessful initialization and cleans partial resources from early-return HUD construction before retry. See the exported transition audit: 1168 extracted publication/destructor assertions, two old-code negative controls, affected 564 Dispatch checks. Full Windows validation pending. This bounded fix does not close admitted Init transaction consistency or GPU queue/fence retirement; main synchronization remains pending those scoped boundaries.

‎docs/NGX_EXPORTED_TRANSITIONS_20260927.md‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,4 +169,11 @@ New actual helper/field extraction test uses production ProviderPublication, wit
169169
Next GPU boundary mapping: IFGFeature_Dx12 has _uiFence and signals after executing its own UI command lists, but HUD Dispatch records into a caller-supplied command list. That UI fence alone does not prove completion of this caller's HUD work. Current D3D12Hooks exposes device/command-list state hooks, not an ExecuteCommandLists completion owner; no queue/fence parameter was found in framegen/nvngx. Do not repurpose the unrelated UI fence or wait on the CPU as a substitute for submission ownership. A follow-up must identify the actual submitting queue and command-list generation or conservatively scope the optional effect.
170170
### Final device/admission/publication validation
171171

172-
2026-10-02 confirmation: 6832332f and final a9f28434 are pushed. First Windows run 36849028967 succeeded. Final run 36849297779 / job 110326885532 and format 36849297777 succeeded, including full DLL, runtime inventory, all configured compatibility checks, package/upload. MSVC confirms 564 HUD Dispatch assertions, 8 HUD publication checks and 42 API handle checks. Artifact OptiScaler_Aurora_v1.0_20261001_compat_a9f28434.7z, id 11154374282, 219573606 bytes, Actions digest sha256:a381566f2f38de5c891ddc81d61cb12ea126709bd2af2a39dcfb5dfc5993f003. Main merge and Release have not occurred. Continue GPU submission/retirement ownership and partial Init transactions; do not recreate these completed CPU/device/publication guards.
172+
2026-10-02 confirmation: 6832332f and final a9f28434 are pushed. First Windows run 36849028967 succeeded. Final run 36849297779 / job 110326885532 and format 36849297777 succeeded, including full DLL, runtime inventory, all configured compatibility checks, package/upload. MSVC confirms 564 HUD Dispatch assertions, 8 HUD publication checks and 42 API handle checks. Artifact OptiScaler_Aurora_v1.0_20261001_compat_a9f28434.7z, id 11154374282, 219573606 bytes, Actions digest sha256:a381566f2f38de5c891ddc81d61cb12ea126709bd2af2a39dcfb5dfc5993f003. Main merge and Release have not occurred. Continue GPU submission/retirement ownership and partial Init transactions; do not recreate these completed CPU/device/publication guards.
173+
## 2026-10-02 HUD initialization retry and partial cleanup
174+
175+
The NGX HUD publication factory previously accepted a non-null context even when `IsInit()` was false. D3D12 setup methods normally report HRESULT failure through early constructor returns, so this permanently cached a failed optional pass. The factory now rejects that candidate, allowing a later call to retry. Published ready contexts retain their existing immutable identity.
176+
177+
`HudCopy_Dx12` now releases its partially allocated pipeline/root-signature/constant-buffer resources when an early-return constructor left `_init=false`. The base shader destructor deliberately skips this state; the derived cleanup prevents retry-related leaks without changing other shader classes. Heap releases are nulling and remain safe with their existing member destructors. Process-shutdown behavior is unchanged. This is cleanup of completed, unsuccessful construction, not a general guarantee for exceptions thrown midway through construction or GPU retirement.
178+
179+
The extracted-helper/destructor harness passes 1168 counted assertions (including all 64 allocation combinations in initialized/uninitialized states); the count includes per-resource release checks, not 1168 distinct GPU scenarios. Replacing only the publication helper with HEAD's old helper fails check 9. Replacing only the HUD destructor with its old body fails check 23. No production source was reverted for these negative controls. The affected Dispatch suite still passes 564 checks. These use COM/constructor stand-ins and actual helper/destructor bodies; they do not inject failures into a real D3D12 device. Full Windows validation pending after push.

‎tests/test_hud_publication.py‎

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,37 @@
1515
#include <stdexcept>
1616
#include <cstdlib>
1717
int checks=0,constructed=0,destroyed=0;
18-
bool fail=false;
18+
bool fail=false,unready=false;
1919
std::function<void()> onConstruct;
2020
void check(bool b){if(!b){std::cerr<<"HUD publication check "<<checks+1<<" failed\n";std::exit(2);}++checks;}
2121
struct ID3D12Device{};
2222
struct HudCopy_Dx12 {
2323
ID3D12Device* device;bool ready=false;
24-
HudCopy_Dx12(const char*,ID3D12Device* d):device(d){++constructed;if(onConstruct)onConstruct();if(fail)throw std::runtime_error("construction");ready=true;}
24+
HudCopy_Dx12(const char*,ID3D12Device* d):device(d){++constructed;if(onConstruct)onConstruct();if(fail)throw std::runtime_error("construction");ready=!unready;}
25+
bool IsInit()const{return ready;}
2526
~HudCopy_Dx12(){++destroyed;}
2627
};
2728
struct Harness {
2829
// FIELD
2930
// HELPER
3031
};
32+
// Exercise the actual HUD and base destructors with counted COM stand-ins.
33+
struct State {bool isShuttingDown=false;static State& Instance(){static State s;return s;}};
34+
struct Resource {int releases=0;void Release(){check(++releases==1);}};
35+
#define SAFE_RELEASE(p) do {if(p){(p)->Release();(p)=nullptr;}}while(false)
36+
constexpr int HudCopy_NUM_OF_HEAPS=2;
37+
struct Heap {Resource* resource=nullptr;void ReleaseHeaps(){SAFE_RELEASE(resource);}~Heap(){ReleaseHeaps();}};
38+
struct Shader_Dx12 {
39+
bool _init=false;
40+
Resource *_pipelineState=nullptr,*_rootSignature=nullptr,*_constantBuffer=nullptr;
41+
~Shader_Dx12();
42+
};
43+
struct CleanupHud:Shader_Dx12 {
44+
Heap _frameHeaps[HudCopy_NUM_OF_HEAPS];Resource* _buffer=nullptr;
45+
~CleanupHud();
46+
};
47+
// BASE_DESTRUCTOR
48+
// HUD_DESTRUCTOR
3149
int main(){
3250
ID3D12Device first,second;
3351
{
@@ -50,6 +68,24 @@
5068
check(worker.get()==published&&constructed==2);
5169
}
5270
check(destroyed==1);
71+
{
72+
Harness h;unready=true;int start=constructed,retired=destroyed;
73+
for(int i=1;i<=3;++i){check(h.GetHudCopy(&first)==nullptr);check(constructed==start+i&&destroyed==retired+i);}
74+
unready=false;auto* retry=h.GetHudCopy(&second);
75+
check(retry&&retry->IsInit()&&retry->device==&second);
76+
check(h.GetHudCopy(&first)==retry&&constructed==start+4);
77+
}
78+
// All partial allocation combinations, then the normal initialized path.
79+
for(bool ready:{false,true})for(int mask=0;mask<64;++mask){
80+
Resource r[6];
81+
{
82+
CleanupHud h;h._init=ready;
83+
if(mask&1)h._rootSignature=&r[0];if(mask&2)h._constantBuffer=&r[1];
84+
if(mask&4)h._pipelineState=&r[2];if(mask&8)h._frameHeaps[0].resource=&r[3];
85+
if(mask&16)h._frameHeaps[1].resource=&r[4];if(mask&32)h._buffer=&r[5];
86+
}
87+
for(int i=0;i<6;++i)check(r[i].releases==((mask>>i)&1));
88+
}
5389
std::cout<<"PASS: "<<checks<<" HUD publication checks\n";
5490
}
5591
'''
@@ -59,9 +95,13 @@ def main():
5995
header=(ROOT/'OptiScaler/framegen/nvngx/Nvngx_FG.h').read_text(encoding='utf-8')
6096
field=re.search(r'static inline ProviderPublication<HudCopy_Dx12> _hudCopy;',header).group().replace('static inline ','')
6197
helper=function(header,'static HudCopy_Dx12* GetHudCopy(').replace('static ','',1)
98+
hud=(ROOT/'OptiScaler/shaders/hud_copy/HudCopy_Dx12.cpp').read_text(encoding='utf-8')
99+
base=(ROOT/'OptiScaler/shaders/Shader_Dx12.cpp').read_text(encoding='utf-8')
100+
hud_destructor=function(hud,'HudCopy_Dx12::~HudCopy_Dx12()').replace('HudCopy_Dx12','CleanupHud')
101+
base_destructor=function(base,'Shader_Dx12::~Shader_Dx12()')
62102
with tempfile.TemporaryDirectory(prefix='aurora-hud-publication-') as folder:
63103
path=Path(folder);cpp=path/'test.cpp';exe=path/'test.exe'
64-
cpp.write_text(PRELUDE.replace('// FIELD',field).replace('// HELPER',helper),encoding='utf-8')
104+
cpp.write_text(PRELUDE.replace('// FIELD',field).replace('// HELPER',helper).replace('// BASE_DESTRUCTOR',base_destructor).replace('// HUD_DESTRUCTOR',hud_destructor),encoding='utf-8')
65105
command=[args.compiler]+([args.driver] if args.driver else [])
66106
if Path(args.compiler).stem.lower()=='cl':command+=['/nologo','/EHsc','/std:c++20','/I'+str(ROOT/'OptiScaler'),str(cpp),'/Fe:'+str(exe)]
67107
else:command+=['-std=c++20','-I'+str(ROOT/'OptiScaler'),str(cpp),'-o',str(exe)]

0 commit comments

Comments
 (0)