From e161c6eeacdea3e4a6d6c22ae129cd2ed80a9d59 Mon Sep 17 00:00:00 2001 From: Martin Valigursky Date: Wed, 6 May 2026 17:24:40 +0100 Subject: [PATCH] fix: Register StorageBuffer for WebGPU device lose/restore Track storage buffers on GraphicsDevice with loseContext/restoreContext. GraphicsDevice.restoreContext calls buffer.restoreContext(); vertex/index delegate to unlock. WebgpuBuffer loseContext clears handle only; allocate applies debug labels. --- src/platform/graphics/graphics-device.js | 4 +-- src/platform/graphics/index-buffer.js | 10 ++++++ src/platform/graphics/storage-buffer.js | 34 +++++++++++++++---- src/platform/graphics/vertex-buffer.js | 10 ++++++ src/platform/graphics/webgpu/webgpu-buffer.js | 18 +++++----- 5 files changed, 59 insertions(+), 17 deletions(-) diff --git a/src/platform/graphics/graphics-device.js b/src/platform/graphics/graphics-device.js index 6400ce8ca08..a65848c40b9 100644 --- a/src/platform/graphics/graphics-device.js +++ b/src/platform/graphics/graphics-device.js @@ -784,9 +784,9 @@ class GraphicsDevice extends EventHandler { this.initializeRenderState(); this.initializeContextCaches(); - // Recreate buffer objects and reupload buffer data to the GPU + // Recreate buffer GPU objects; vertex/index reupload from CPU storage, storage buffers empty for (const buffer of this.buffers) { - buffer.unlock(); + buffer.restoreContext(); } this.gpuProfiler?.restoreContext?.(); diff --git a/src/platform/graphics/index-buffer.js b/src/platform/graphics/index-buffer.js index 803bbaa4177..cd70292fbea 100644 --- a/src/platform/graphics/index-buffer.js +++ b/src/platform/graphics/index-buffer.js @@ -112,6 +112,16 @@ class IndexBuffer { this.impl.loseContext(); } + /** + * Called when the rendering context is restored. Recreates the GPU buffer and uploads from + * {@link IndexBuffer#lock|lock} storage. + * + * @ignore + */ + restoreContext() { + this.unlock(); + } + /** * Returns the data format of the specified index buffer. * diff --git a/src/platform/graphics/storage-buffer.js b/src/platform/graphics/storage-buffer.js index 8e4e10546dc..ff79f60b52f 100644 --- a/src/platform/graphics/storage-buffer.js +++ b/src/platform/graphics/storage-buffer.js @@ -13,6 +13,10 @@ let id = 0; * used to provide data for compute shader, and to store the result of the computation. * Note that this class is only supported on the WebGPU platform. * + * After a graphics device is lost and restored, the GPU backing for a storage buffer is + * recreated at the same byte size but its contents are undefined until you write to it again or + * repopulate it via compute. + * * @category Graphics */ class StorageBuffer { @@ -39,10 +43,7 @@ class StorageBuffer { this.impl = graphicsDevice.createBufferImpl(usage); this.impl.allocate(graphicsDevice, byteSize); - // Note: not registered in device.buffers — storage buffer contents are not - // recoverable on device-lost (no CPU-side shadow, may be GPU-written), so - // they don't participate in the engine's auto lose/restore iteration. - // Consumers handling device-lost must destroy() and recreate storage buffers. + graphicsDevice.buffers.add(this); this.adjustVramSizeTracking(graphicsDevice._vram, this.byteSize); } @@ -51,8 +52,29 @@ class StorageBuffer { * Frees resources associated with this storage buffer. */ destroy() { - this.adjustVramSizeTracking(this.device._vram, -this.byteSize); - this.impl.destroy(this.device); + const device = this.device; + device.buffers.delete(this); + this.impl.destroy(device); + this.adjustVramSizeTracking(device._vram, -this.byteSize); + } + + /** + * Called when the rendering context was lost. It releases the GPU buffer handle. + * + * @ignore + */ + loseContext() { + this.impl.loseContext(); + } + + /** + * Called when the rendering context is restored. Recreates an empty GPU buffer of the same + * size; contents are not restored from CPU memory. + * + * @ignore + */ + restoreContext() { + this.impl.allocate(this.device, this.byteSize); } adjustVramSizeTracking(vram, size) { diff --git a/src/platform/graphics/vertex-buffer.js b/src/platform/graphics/vertex-buffer.js index 4ac9b314b8a..cc31480d78a 100644 --- a/src/platform/graphics/vertex-buffer.js +++ b/src/platform/graphics/vertex-buffer.js @@ -91,6 +91,16 @@ class VertexBuffer { this.impl.loseContext(); } + /** + * Called when the rendering context is restored. Recreates the GPU buffer and uploads from + * {@link VertexBuffer#lock|lock} storage. + * + * @ignore + */ + restoreContext() { + this.unlock(); + } + /** * Returns the data format of the specified vertex buffer. * diff --git a/src/platform/graphics/webgpu/webgpu-buffer.js b/src/platform/graphics/webgpu/webgpu-buffer.js index 7aa47fc2445..7231a24d568 100644 --- a/src/platform/graphics/webgpu/webgpu-buffer.js +++ b/src/platform/graphics/webgpu/webgpu-buffer.js @@ -37,6 +37,7 @@ class WebgpuBuffer { } loseContext() { + this.buffer = null; } allocate(device, size) { @@ -45,6 +46,14 @@ class WebgpuBuffer { size, usage: this.usageFlags }); + + DebugHelper.setLabel(this.buffer, + this.usageFlags & GPUBufferUsage.VERTEX ? 'VertexBuffer' : + this.usageFlags & GPUBufferUsage.INDEX ? 'IndexBuffer' : + this.usageFlags & GPUBufferUsage.UNIFORM ? 'UniformBuffer' : + this.usageFlags & GPUBufferUsage.STORAGE ? 'StorageBuffer' : + '' + ); } /** @@ -66,15 +75,6 @@ class WebgpuBuffer { this.usageFlags |= GPUBufferUsage.COPY_DST; this.allocate(device, size); - DebugHelper.setLabel(this.buffer, - this.usageFlags & GPUBufferUsage.VERTEX ? 'VertexBuffer' : - this.usageFlags & GPUBufferUsage.INDEX ? 'IndexBuffer' : - this.usageFlags & GPUBufferUsage.UNIFORM ? 'UniformBuffer' : - this.usageFlags & GPUBufferUsage.STORAGE ? 'StorageBuffer' : - '' - ); - - // mappedAtCreation path - this could be used when the data is provided // this.buffer = device.wgpu.createBuffer({