Repository: denoland/deno
Pull request: feat(UNSTABLE/byow): add resize method for UnsafeWindowSurface

In the WebGPU specification, we can find that when the canvas resizes, the WebGPU context would reconfigure automatically[^resize].

When using wgpu, when the window size changes, the surface should be reconfigured with the appropriate width and height[^wgpu-resize].

After comprehensive consideration, I chose this solution, adding a `resize` method to `UnsafeWindowSurface` to automatically reconfigure the surface or canvas when invoked.

The following are questions you may ask:

> Why not put this method on `GPUCanvasContext` or adding optional `width` and `height` members to `GPUCanvasConfiguration`?

- To be consistent with the specification, `GPUCanvasConfiguration` in the specification does not have `width` and `height` members;
- To be consistent with the `present` method. Prestation and resizing are handled automatically on the web platform. Deno needs to handle them manually, so it is better to put them both on `UnsafeWindowSurface`.

> Why not make this method simpler, just update the `width` and `height` of the surface, and then have the programmer manually call `GPUCanvasContext.configure`?

- This requires the programmer to save a copy of `GPUCanvasConfiguration`, which increases the complexity for users;
- These two steps are always done together. It is more efficient to combine them and implement it in Rust;

> Why add `surface_configure` member to `surface::Configuration`? Is it possible to remove the `usage` and `format` members?

There is currently no easy way to convert `GPUTextureUsageFlags` to `wgpu_types::TextureUsages`, nor is there a way to convert `GPUTextureFormat` to `wgpu_types::TextureFormat`.

In the future `GPUCanvasContext.getConfiguration()`[^getconf] would be implemented, so saving a copy of the configuration is necessary.

[^resize]: https://www.w3.org/TR/2025/CRD-webgpu-20250430/#context-sizing

[^wgpu-resize]: https://sotrh.github.io/learn-wgpu/beginner/tutorial2-surface/#resize

[^getconf]: https://www.w3.org/TR/webgpu/#dom-gpucanvascontext-getconfiguration

---

close 28132.


Changed files:
filename: cli/tsc/dts/lib.deno.unstable.d.ts
patch: @@ -38,6 +38,10 @@ declare namespace Deno {
     );
     getContext(context: "webgpu"): GPUCanvasContext;
     present(): void;
+    /**
+     * This method should be invoked when the size of the window changes.
+     */
+    resize(width: number, height: number): void;
   }
 
   /** **UNSTABLE**: New API, yet to be vetted.

filename: ext/webgpu/byow.rs
patch: @@ -161,6 +161,18 @@ impl UnsafeWindowSurface {
 
     context.present().map_err(JsErrorBox::from_err)
   }
+
+  #[fast]
+  fn resize(&self, width: u32, height: u32, scope: &mut v8::HandleScope) {
+    self.width.replace(width);
+    self.height.replace(height);
+
+    let Some(context) = self.context.try_unwrap(scope) else {
+      return;
+    };
+
+    context.resize_configure(width, height);
+  }
 }
 
 struct UnsafeWindowSurfaceOptions {

filename: ext/webgpu/surface.rs
patch: @@ -32,6 +32,8 @@ pub struct Configuration {
   pub device: Ptr<GPUDevice>,
   pub usage: u32,
   pub format: GPUTextureFormat,
+  pub surface_config:
+    wgpu_types::SurfaceConfiguration<Vec<wgpu_types::TextureFormat>>,
 }
 
 pub struct GPUCanvasContext {
@@ -97,6 +99,7 @@ impl GPUCanvasContext {
       device,
       usage: configuration.usage,
       format: configuration.format,
+      surface_config: conf,
     });
 
     Ok(())
@@ -175,6 +178,27 @@ impl GPUCanvasContext {
 
     Ok(())
   }
+
+  pub fn resize_configure(&self, width: u32, height: u32) {
+    self.width.replace(width);
+    self.height.replace(height);
+
+    let mut config = self.config.borrow_mut();
+    let Some(config) = &mut *config else {
+      return;
+    };
+
+    config.surface_config.width = width;
+    config.surface_config.height = height;
+
+    let err = config.device.instance.surface_configure(
+      self.surface_id,
+      config.device.id,
+      &config.surface_config,
+    );
+
+    config.device.error_handler.push_error(err);
+  }
 }
 
 #[derive(WebIDL)]