From e2084c880c519fd724ecaa431158ba19af81cc87 Mon Sep 17 00:00:00 2001 From: Nico Martin Date: Thu, 12 Mar 2026 17:18:45 +0100 Subject: [PATCH 1/3] fixed webgpu device destroyed on session release --- js/web/lib/wasm/jsep/backend-webgpu.ts | 15 ++++++++++++++- .../core/providers/webgpu/webgpu_context.h | 10 +++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/js/web/lib/wasm/jsep/backend-webgpu.ts b/js/web/lib/wasm/jsep/backend-webgpu.ts index e486e4b0e043d..f10d5a2198eb0 100644 --- a/js/web/lib/wasm/jsep/backend-webgpu.ts +++ b/js/web/lib/wasm/jsep/backend-webgpu.ts @@ -278,7 +278,7 @@ export class WebGpuBackend { value: this.device, writable: false, enumerable: true, - configurable: false, + configurable: true, // Allow deletion when device is destroyed }); Object.defineProperty(this.env.webgpu, 'adapter', { value: adapter, @@ -296,6 +296,19 @@ export class WebGpuBackend { this.querySet.destroy(); } this.gpuDataManager.dispose(); + + // Clear the device reference when it's destroyed to allow new sessions to create a fresh device + // This handles the case where preserve_device=false (default) causes the C++ side to destroy the device + if (this.device && this.env?.webgpu) { + // Check if device is destroyed by listening to the 'lost' promise + void this.device.lost.then((info) => { + // Device was destroyed (reason 'destroyed' means explicitly destroyed via device.destroy()) + if (info.reason === 'destroyed') { + // Clear the device reference so next session creation can acquire a new device + delete (this.env.webgpu as unknown as Record).device; + } + }); + } } getCommandEncoder(): GPUCommandEncoder { diff --git a/onnxruntime/core/providers/webgpu/webgpu_context.h b/onnxruntime/core/providers/webgpu/webgpu_context.h index 7645f1e6b2482..eb162c217ac39 100644 --- a/onnxruntime/core/providers/webgpu/webgpu_context.h +++ b/onnxruntime/core/providers/webgpu/webgpu_context.h @@ -95,7 +95,15 @@ struct WebGpuContextConfig { webgpu::ValidationMode::Basic // for release build, enable basic validation by default #endif // !NDEBUG }; - bool preserve_device{false}; + // For WASM builds, preserve device by default to support create->release->create pattern + // Device creation is expensive in browsers (adapter enumeration) and most web apps reuse devices + bool preserve_device{ +#ifdef __EMSCRIPTEN__ + true // Default ON for web builds +#else + false // Default OFF for native builds +#endif + }; uint64_t max_storage_buffer_binding_size{0}; WebGpuBufferCacheConfig buffer_cache_config{}; int power_preference{static_cast(WGPUPowerPreference_HighPerformance)}; From cbd49fa1e91d453e237712cdf96fb2ba28012065 Mon Sep 17 00:00:00 2001 From: Nico Martin Date: Thu, 12 Mar 2026 17:46:50 +0100 Subject: [PATCH 2/3] simplify fix --- js/web/lib/wasm/jsep/backend-webgpu.ts | 11 +++-------- onnxruntime/core/providers/webgpu/webgpu_context.h | 10 +--------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/js/web/lib/wasm/jsep/backend-webgpu.ts b/js/web/lib/wasm/jsep/backend-webgpu.ts index f10d5a2198eb0..c3609576704fb 100644 --- a/js/web/lib/wasm/jsep/backend-webgpu.ts +++ b/js/web/lib/wasm/jsep/backend-webgpu.ts @@ -297,16 +297,11 @@ export class WebGpuBackend { } this.gpuDataManager.dispose(); - // Clear the device reference when it's destroyed to allow new sessions to create a fresh device + // Clear the device reference when it's lost to allow new sessions to create a fresh device // This handles the case where preserve_device=false (default) causes the C++ side to destroy the device if (this.device && this.env?.webgpu) { - // Check if device is destroyed by listening to the 'lost' promise - void this.device.lost.then((info) => { - // Device was destroyed (reason 'destroyed' means explicitly destroyed via device.destroy()) - if (info.reason === 'destroyed') { - // Clear the device reference so next session creation can acquire a new device - delete (this.env.webgpu as unknown as Record).device; - } + this.device.lost.then(() => { + delete (this.env.webgpu as unknown as Record).device; }); } } diff --git a/onnxruntime/core/providers/webgpu/webgpu_context.h b/onnxruntime/core/providers/webgpu/webgpu_context.h index 511c5a4576c89..6b9b538483f16 100644 --- a/onnxruntime/core/providers/webgpu/webgpu_context.h +++ b/onnxruntime/core/providers/webgpu/webgpu_context.h @@ -95,15 +95,7 @@ struct WebGpuContextConfig { webgpu::ValidationMode::Basic // for release build, enable basic validation by default #endif // !NDEBUG }; - // For WASM builds, preserve device by default to support create->release->create pattern - // Device creation is expensive in browsers (adapter enumeration) and most web apps reuse devices - bool preserve_device{ -#ifdef __EMSCRIPTEN__ - true // Default ON for web builds -#else - false // Default OFF for native builds -#endif - }; + bool preserve_device{false}; uint64_t max_storage_buffer_binding_size{0}; WebGpuBufferCacheConfig buffer_cache_config{}; int power_preference{static_cast(WGPUPowerPreference_HighPerformance)}; From 507c5d086a4c4e4bdbe86720cce2f79591f8ad10 Mon Sep 17 00:00:00 2001 From: Nico Martin Date: Thu, 12 Mar 2026 18:45:01 +0100 Subject: [PATCH 3/3] fixed Web CI Pipeline / precheck --- js/web/lib/wasm/jsep/backend-webgpu.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/web/lib/wasm/jsep/backend-webgpu.ts b/js/web/lib/wasm/jsep/backend-webgpu.ts index c3609576704fb..bc5d691b19d4d 100644 --- a/js/web/lib/wasm/jsep/backend-webgpu.ts +++ b/js/web/lib/wasm/jsep/backend-webgpu.ts @@ -300,7 +300,7 @@ export class WebGpuBackend { // Clear the device reference when it's lost to allow new sessions to create a fresh device // This handles the case where preserve_device=false (default) causes the C++ side to destroy the device if (this.device && this.env?.webgpu) { - this.device.lost.then(() => { + void this.device.lost.then(() => { delete (this.env.webgpu as unknown as Record).device; }); }