diff --git a/Cargo.toml b/Cargo.toml index f4bcfcb353..5d388196b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -172,7 +172,7 @@ hkdf = "0.12.3" # webgpu raw-window-handle = "0.5.0" -wgpu-core = "=0.18" +wgpu-core = { version = "=0.18", features = ["raw-window-handle"] } wgpu-types = "=0.18" wgpu-hal = "=0.18" diff --git a/ext/webgpu/00_init.js b/ext/webgpu/00_init.js new file mode 100644 index 0000000000..b559fe3db0 --- /dev/null +++ b/ext/webgpu/00_init.js @@ -0,0 +1,39 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { core } from "ext:core/mod.js"; +const ops = core.ops; + +let webgpu; + +function webGPUNonEnumerable(getter) { + let valueIsSet = false; + let value; + + return { + get() { + loadWebGPU(); + + if (valueIsSet) { + return value; + } else { + return getter(); + } + }, + set(v) { + loadWebGPU(); + + valueIsSet = true; + value = v; + }, + enumerable: false, + configurable: true, + }; +} + +function loadWebGPU() { + if (!webgpu) { + webgpu = ops.op_lazy_load_esm("ext:deno_webgpu/01_webgpu.js"); + } +} + +export { loadWebGPU, webgpu, webGPUNonEnumerable }; diff --git a/ext/webgpu/01_webgpu.js b/ext/webgpu/01_webgpu.js index c8258621f8..0dd660d428 100644 --- a/ext/webgpu/01_webgpu.js +++ b/ext/webgpu/01_webgpu.js @@ -7036,6 +7036,78 @@ webidl.converters["GPUSignedOffset32"] = (V, opts) => // TYPEDEF: GPUFlagsConstant webidl.converters["GPUFlagsConstant"] = webidl.converters["unsigned long"]; +// ENUM: GPUCanvasAlphaMode +webidl.converters["GPUCanvasAlphaMode"] = webidl.createEnumConverter( + "GPUCanvasAlphaMode", + [ + "opaque", + "premultiplied", + ], +); + +// NON-SPEC: ENUM: GPUPresentMode +webidl.converters["GPUPresentMode"] = webidl.createEnumConverter( + "GPUPresentMode", + [ + "autoVsync", + "autoNoVsync", + "fifo", + "fifoRelaxed", + "immediate", + "mailbox", + ], +); + +// DICT: GPUCanvasConfiguration +const dictMembersGPUCanvasConfiguration = [ + { key: "device", converter: webidl.converters.GPUDevice, required: true }, + { + key: "format", + converter: webidl.converters.GPUTextureFormat, + required: true, + }, + { + key: "usage", + converter: webidl.converters["GPUTextureUsageFlags"], + defaultValue: GPUTextureUsage.RENDER_ATTACHMENT, + }, + { + key: "alphaMode", + converter: webidl.converters["GPUCanvasAlphaMode"], + defaultValue: "opaque", + }, + + // Extended from spec + { + key: "presentMode", + converter: webidl.converters["GPUPresentMode"], + }, + { + key: "width", + converter: webidl.converters["long"], + required: true, + }, + { + key: "height", + converter: webidl.converters["long"], + required: true, + }, + { + key: "viewFormats", + converter: webidl.createSequenceConverter( + webidl.converters["GPUTextureFormat"], + ), + get defaultValue() { + return []; + }, + }, +]; +webidl.converters["GPUCanvasConfiguration"] = webidl + .createDictionaryConverter( + "GPUCanvasConfiguration", + dictMembersGPUCanvasConfiguration, + ); + const gpu = webidl.createBranded(GPU); export { _device, diff --git a/ext/webgpu/02_surface.js b/ext/webgpu/02_surface.js index fb767e52a4..9ae7fb54d6 100644 --- a/ext/webgpu/02_surface.js +++ b/ext/webgpu/02_surface.js @@ -11,22 +11,16 @@ const ops = core.ops; import * as webidl from "ext:deno_webidl/00_webidl.js"; import { createFilteredInspectProxy } from "ext:deno_console/01_console.js"; const { Symbol, SymbolFor, ObjectPrototypeIsPrototypeOf } = primordials; -import { - _device, - assertDevice, - createGPUTexture, - GPUTextureUsage, -} from "ext:deno_webgpu/01_webgpu.js"; +import { loadWebGPU, webgpu } from "ext:deno_webgpu/00_init.js"; const _surfaceRid = Symbol("[[surfaceRid]]"); const _configuration = Symbol("[[configuration]]"); const _canvas = Symbol("[[canvas]]"); const _currentTexture = Symbol("[[currentTexture]]"); +const _present = Symbol("[[present]]"); class GPUCanvasContext { /** @type {number} */ [_surfaceRid]; - /** @type {InnerGPUDevice} */ - [_device]; [_configuration]; [_canvas]; /** @type {GPUTexture | undefined} */ @@ -50,6 +44,7 @@ class GPUCanvasContext { context: "Argument 1", }); + const { _device, assertDevice } = webgpu; this[_device] = configuration.device[_device]; this[_configuration] = configuration; const device = assertDevice(this, { @@ -72,6 +67,8 @@ class GPUCanvasContext { } unconfigure() { + const { _device } = webgpu; + webidl.assertBranded(this, GPUCanvasContextPrototype); this[_configuration] = null; @@ -86,6 +83,7 @@ class GPUCanvasContext { if (this[_configuration] === null) { throw new DOMException("context is not configured.", "InvalidStateError"); } + const { createGPUTexture, assertDevice } = webgpu; const device = assertDevice(this, { prefix, context: "this" }); @@ -119,8 +117,10 @@ class GPUCanvasContext { return texture; } - // Extended from spec. Required to present the texture; browser don't need this. - present() { + // Required to present the texture; browser don't need this. + [_present]() { + const { assertDevice } = webgpu; + webidl.assertBranded(this, GPUCanvasContextPrototype); const prefix = "Failed to execute 'present' on 'GPUCanvasContext'"; const device = assertDevice(this[_currentTexture], { @@ -148,88 +148,17 @@ class GPUCanvasContext { const GPUCanvasContextPrototype = GPUCanvasContext.prototype; function createCanvasContext(options) { + // lazy load webgpu if needed + loadWebGPU(); + const canvasContext = webidl.createBranded(GPUCanvasContext); canvasContext[_surfaceRid] = options.surfaceRid; canvasContext[_canvas] = options.canvas; return canvasContext; } -// Converters +function presentGPUCanvasContext(ctx) { + ctx[_present](); +} -// ENUM: GPUCanvasAlphaMode -webidl.converters["GPUCanvasAlphaMode"] = webidl.createEnumConverter( - "GPUCanvasAlphaMode", - [ - "opaque", - "premultiplied", - ], -); - -// NON-SPEC: ENUM: GPUPresentMode -webidl.converters["GPUPresentMode"] = webidl.createEnumConverter( - "GPUPresentMode", - [ - "autoVsync", - "autoNoVsync", - "fifo", - "fifoRelaxed", - "immediate", - "mailbox", - ], -); - -// DICT: GPUCanvasConfiguration -const dictMembersGPUCanvasConfiguration = [ - { key: "device", converter: webidl.converters.GPUDevice, required: true }, - { - key: "format", - converter: webidl.converters.GPUTextureFormat, - required: true, - }, - { - key: "usage", - converter: webidl.converters["GPUTextureUsageFlags"], - defaultValue: GPUTextureUsage.RENDER_ATTACHMENT, - }, - { - key: "alphaMode", - converter: webidl.converters["GPUCanvasAlphaMode"], - defaultValue: "opaque", - }, - - // Extended from spec - { - key: "presentMode", - converter: webidl.converters["GPUPresentMode"], - }, - { - key: "width", - converter: webidl.converters["long"], - required: true, - }, - { - key: "height", - converter: webidl.converters["long"], - required: true, - }, - { - key: "viewFormats", - converter: webidl.createSequenceConverter( - webidl.converters["GPUTextureFormat"], - ), - get defaultValue() { - return []; - }, - }, -]; -webidl.converters["GPUCanvasConfiguration"] = webidl - .createDictionaryConverter( - "GPUCanvasConfiguration", - dictMembersGPUCanvasConfiguration, - ); - -window.__bootstrap.webgpu = { - ...window.__bootstrap.webgpu, - GPUCanvasContext, - createCanvasContext, -}; +export { createCanvasContext, GPUCanvasContext, presentGPUCanvasContext }; diff --git a/ext/webgpu/Cargo.toml b/ext/webgpu/Cargo.toml index accee5ed98..461eddf5e8 100644 --- a/ext/webgpu/Cargo.toml +++ b/ext/webgpu/Cargo.toml @@ -13,9 +13,6 @@ description = "WebGPU implementation for Deno" [lib] path = "lib.rs" -[features] -surface = ["wgpu-core/raw-window-handle", "dep:raw-window-handle"] - # We make all dependencies conditional on not being wasm, # so the whole workspace can built as wasm. [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/ext/webgpu/error.rs b/ext/webgpu/error.rs index be8021f87e..5d8abb4a3c 100644 --- a/ext/webgpu/error.rs +++ b/ext/webgpu/error.rs @@ -23,7 +23,6 @@ use wgpu_core::device::DeviceError; use wgpu_core::pipeline::CreateComputePipelineError; use wgpu_core::pipeline::CreateRenderPipelineError; use wgpu_core::pipeline::CreateShaderModuleError; -#[cfg(feature = "surface")] use wgpu_core::present::ConfigureSurfaceError; use wgpu_core::resource::BufferAccessError; use wgpu_core::resource::CreateBufferError; @@ -282,7 +281,6 @@ impl From for WebGpuError { } } -#[cfg(feature = "surface")] impl From for WebGpuError { fn from(err: ConfigureSurfaceError) -> Self { WebGpuError::Validation(fmt_err(&err)) diff --git a/ext/webgpu/lib.rs b/ext/webgpu/lib.rs index 834ac6bcc7..abb36fb8dd 100644 --- a/ext/webgpu/lib.rs +++ b/ext/webgpu/lib.rs @@ -75,7 +75,6 @@ pub mod queue; pub mod render_pass; pub mod sampler; pub mod shader; -#[cfg(feature = "surface")] pub mod surface; pub mod texture; @@ -212,7 +211,12 @@ deno_core::extension!( queue::op_webgpu_write_texture, // shader shader::op_webgpu_create_shader_module, + // surface + surface::op_webgpu_surface_configure, + surface::op_webgpu_surface_get_current_texture, + surface::op_webgpu_surface_present ], + esm = ["00_init.js", "02_surface.js"], lazy_loaded_esm = ["01_webgpu.js"], ); diff --git a/ext/webgpu/surface.rs b/ext/webgpu/surface.rs index 4f243fbb1d..1371c1fa48 100644 --- a/ext/webgpu/surface.rs +++ b/ext/webgpu/surface.rs @@ -11,17 +11,6 @@ use std::borrow::Cow; use std::rc::Rc; use wgpu_types::SurfaceStatus; -deno_core::extension!( - deno_webgpu_surface, - deps = [deno_webidl, deno_web, deno_webgpu], - ops = [ - op_webgpu_surface_configure, - op_webgpu_surface_get_current_texture, - op_webgpu_surface_present, - ], - esm = ["02_surface.js"], -); - pub struct WebGpuSurface(pub crate::Instance, pub wgpu_core::id::SurfaceId); impl Resource for WebGpuSurface { fn name(&self) -> Cow { diff --git a/runtime/js/98_global_scope.js b/runtime/js/98_global_scope.js index 14b11cbde4..de83195282 100644 --- a/runtime/js/98_global_scope.js +++ b/runtime/js/98_global_scope.js @@ -42,6 +42,12 @@ import * as globalInterfaces from "ext:deno_web/04_global_interfaces.js"; import * as webStorage from "ext:deno_webstorage/01_webstorage.js"; import * as prompt from "ext:runtime/41_prompt.js"; import * as imageData from "ext:deno_web/16_image_data.js"; +import { + loadWebGPU, + webgpu, + webGPUNonEnumerable, +} from "ext:deno_webgpu/00_init.js"; +import * as webgpuSurface from "ext:deno_webgpu/02_surface.js"; import { unstableIds } from "ext:runtime/90_deno_ns.js"; // https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope @@ -189,6 +195,7 @@ unstableForWindowOrWorkerGlobalScope[unstableIds.webgpu] = { GPUError: webGPUNonEnumerable(() => webgpu.GPUError), GPUValidationError: webGPUNonEnumerable(() => webgpu.GPUValidationError), GPUOutOfMemoryError: webGPUNonEnumerable(() => webgpu.GPUOutOfMemoryError), + GPUCanvasContext: webGPUNonEnumerable(() => webgpuSurface.GPUCanvasContext), }; class Navigator { @@ -229,39 +236,6 @@ const numCpus = memoizeLazy(() => ops.op_bootstrap_numcpus()); const userAgent = memoizeLazy(() => ops.op_bootstrap_user_agent()); const language = memoizeLazy(() => ops.op_bootstrap_language()); -let webgpu; - -function webGPUNonEnumerable(getter) { - let valueIsSet = false; - let value; - - return { - get() { - loadWebGPU(); - - if (valueIsSet) { - return value; - } else { - return getter(); - } - }, - set(v) { - loadWebGPU(); - - valueIsSet = true; - value = v; - }, - enumerable: false, - configurable: true, - }; -} - -function loadWebGPU() { - if (!webgpu) { - webgpu = ops.op_lazy_load_esm("ext:deno_webgpu/01_webgpu.js"); - } -} - ObjectDefineProperties(Navigator.prototype, { gpu: { configurable: true,