From 3035dee9f14402f57d42ff0b362152140b4dca13 Mon Sep 17 00:00:00 2001 From: Leo Kettmeir Date: Mon, 30 Jan 2023 15:14:16 +0100 Subject: [PATCH] chore: update webgpu (#17534) --- Cargo.lock | 81 ++---- Cargo.toml | 5 + cli/tsc/dts/lib.deno_webgpu.d.ts | 58 ++-- ext/webgpu/Cargo.toml | 30 ++- ext/webgpu/src/01_webgpu.js | 360 ++++++++++++++++--------- ext/webgpu/src/02_idl_types.js | 29 +- ext/webgpu/src/03_surface.js | 148 ++++++++++ ext/webgpu/src/04_surface_idl_types.js | 86 ++++++ ext/webgpu/src/binding.rs | 6 +- ext/webgpu/src/buffer.rs | 15 +- ext/webgpu/src/bundle.rs | 24 +- ext/webgpu/src/command_encoder.rs | 3 +- ext/webgpu/src/compute_pass.rs | 4 +- ext/webgpu/src/error.rs | 14 +- ext/webgpu/src/lib.rs | 44 +-- ext/webgpu/src/pipeline.rs | 16 +- ext/webgpu/src/render_pass.rs | 4 +- ext/webgpu/src/sampler.rs | 2 +- ext/webgpu/src/shader.rs | 3 +- ext/webgpu/src/surface.rs | 137 ++++++++++ ext/webgpu/src/texture.rs | 6 +- ext/webgpu/webgpu.idl | 212 ++++++++------- runtime/js/98_global_scope.js | 3 + tools/wgpu_sync.js | 32 ++- 24 files changed, 914 insertions(+), 408 deletions(-) create mode 100644 ext/webgpu/src/03_surface.js create mode 100644 ext/webgpu/src/04_surface_idl_types.js create mode 100644 ext/webgpu/src/surface.rs diff --git a/Cargo.lock b/Cargo.lock index 694fec1e89..bca903286c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,9 +123,9 @@ dependencies = [ [[package]] name = "ash" -version = "0.37.0+1.3.209" +version = "0.37.2+1.3.238" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "006ca68e0f2b03f22d6fa9f2860f85aed430d257fec20f8879b2145e7c7ae1a6" +checksum = "28bf19c1f0a470be5fbf7522a308a05df06610252c5bcf5143e1b23f629a9a03" dependencies = [ "libloading", ] @@ -289,16 +289,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "bitflags_serde_shim" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25c3d626f0280ec39b33a6fc5c6c1067432b4c41e94aee40ded197a6649bf025" -dependencies = [ - "bitflags", - "serde", -] - [[package]] name = "block" version = "0.1.6" @@ -407,12 +397,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - [[package]] name = "chrono" version = "0.4.22" @@ -522,12 +506,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" -[[package]] -name = "copyless" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536" - [[package]] name = "core-foundation" version = "0.9.3" @@ -685,9 +663,9 @@ dependencies = [ [[package]] name = "d3d12" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "827914e1f53b1e0e025ecd3d967a7836b7bcb54520f90e21ef8df7b4d88a2759" +checksum = "d8f0de2f5a8e7bd4a9eec0e3c781992a4ce1724f68aec7d7a3715344de8b39da" dependencies = [ "bitflags", "libloading", @@ -1288,6 +1266,7 @@ name = "deno_webgpu" version = "0.87.0" dependencies = [ "deno_core", + "raw-window-handle", "serde", "tokio", "wgpu-core", @@ -2023,9 +2002,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "glow" -version = "0.11.2" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8bd5877156a19b8ac83a29b2306fe20537429d318f3ff0a1a2119f8d9c61919" +checksum = "8edf6019dff2d92ad27c1e3ff82ad50a0aea5b01370353cc928bfdc33e95925c" dependencies = [ "js-sys", "slotmap", @@ -2363,12 +2342,6 @@ dependencies = [ "generic-array 0.14.6", ] -[[package]] -name = "inplace_it" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e567468c50f3d4bc7397702e09b380139f9b9288b4e909b070571007f8b5bf78" - [[package]] name = "instant" version = "0.1.12" @@ -2798,9 +2771,9 @@ checksum = "f13de1c3edc9a5b9dc3a1029f56e9ab3eba34640010aff4fc01044c42ef67afa" [[package]] name = "naga" -version = "0.9.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f50357e1167a3ab92d6b3c7f4bf5f7fd13fde3f4b28bf0d5ea07b5100fdb6c0" +checksum = "5eafe22a23b797c9bc227c6c896419b26b5bb88fa903417a3adaed08778850d5" dependencies = [ "bit-set", "bitflags", @@ -3504,9 +3477,9 @@ checksum = "63e935c45e09cc6dcf00d2f0b2d630a58f4095320223d47fc68918722f0538b6" [[package]] name = "raw-window-handle" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b800beb9b6e7d2df1fe337c9e3d04e3af22a124460fb4c30fcc22c9117cefb41" +checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a" dependencies = [ "cty", ] @@ -3552,12 +3525,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "renderdoc-sys" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157" - [[package]] name = "reqwest" version = "0.11.11" @@ -3644,9 +3611,9 @@ dependencies = [ [[package]] name = "ron" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" +checksum = "300a51053b1cb55c80b7a9fde4120726ddf25ca241a1cbb926626f62fb136bff" dependencies = [ "base64", "bitflags", @@ -5541,21 +5508,20 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "0.13.2" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b92788dec9d0c1bed849a1b83f01b2ee12819bf04a79c90f68e4173f7b5ba2" +checksum = "be1f61be28e557a6ecb2506cac06c63fae3b6d302a006f38195a7a80995abeb9" dependencies = [ "arrayvec", "bit-vec", "bitflags", - "cfg_aliases", "codespan-reporting", - "copyless", "fxhash", "log", "naga", "parking_lot 0.12.1", "profiling", + "raw-window-handle", "ron", "serde", "smallvec", @@ -5567,9 +5533,9 @@ dependencies = [ [[package]] name = "wgpu-hal" -version = "0.13.2" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cbdfc3d0637dba3d5536b93adef3d26023a0b96f0e1ee5ee9560a401d9f646" +checksum = "82e95792925fe3d58950b9a5c2a191caa145e2bc570e2d233f0d7320f6a8e814" dependencies = [ "android_system_properties", "arrayvec", @@ -5584,9 +5550,9 @@ dependencies = [ "glow", "gpu-alloc", "gpu-descriptor", - "inplace_it", "js-sys", "khronos-egl", + "libc", "libloading", "log", "metal", @@ -5596,7 +5562,7 @@ dependencies = [ "profiling", "range-alloc", "raw-window-handle", - "renderdoc-sys", + "smallvec", "thiserror", "wasm-bindgen", "web-sys", @@ -5606,13 +5572,14 @@ dependencies = [ [[package]] name = "wgpu-types" -version = "0.13.2" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f762cbc08e1a51389859cf9c199c7aef544789cf3510889aab12c607f701604" +checksum = "ecf8cfcbf98f94cc8bd5981544c687140cf9d3948e2ab83849367ead2cd737cf" dependencies = [ "bitflags", - "bitflags_serde_shim", + "js-sys", "serde", + "web-sys", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 503f45aac5..6a60dcf89c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -121,6 +121,11 @@ url = { version = "2.3.1", features = ["serde", "expose_internals"] } uuid = { version = "=1.1.2", features = ["v4"] } zstd = "=0.11.2" +# webgpu +raw-window-handle = "0.5.0" +wgpu-core = "0.15" +wgpu-types = "0.15" + # macros proc-macro2 = "1" quote = "1" diff --git a/cli/tsc/dts/lib.deno_webgpu.d.ts b/cli/tsc/dts/lib.deno_webgpu.d.ts index 0497e24171..150f16babe 100644 --- a/cli/tsc/dts/lib.deno_webgpu.d.ts +++ b/cli/tsc/dts/lib.deno_webgpu.d.ts @@ -22,6 +22,7 @@ declare class GPUSupportedLimits { maxTextureDimension3D?: number; maxTextureArrayLayers?: number; maxBindGroups?: number; + maxBindingsPerBindGroup?: number; maxDynamicUniformBuffersPerPipelineLayout?: number; maxDynamicStorageBuffersPerPipelineLayout?: number; maxSampledTexturesPerShaderStage?: number; @@ -34,6 +35,7 @@ declare class GPUSupportedLimits { minUniformBufferOffsetAlignment?: number; minStorageBufferOffsetAlignment?: number; maxVertexBuffers?: number; + maxBufferSize?: number; maxVertexAttributes?: number; maxVertexBufferArrayStride?: number; maxInterStageShaderComponents?: number; @@ -109,7 +111,6 @@ declare interface GPUDeviceDescriptor extends GPUObjectDescriptorBase { /** @category WebGPU */ declare type GPUFeatureName = | "depth-clip-control" - | "depth24unorm-stencil8" | "depth32float-stencil8" | "pipeline-statistics-query" | "texture-compression-bc" @@ -139,9 +140,6 @@ declare class GPUDevice extends EventTarget implements GPUObjectBase { readonly lost: Promise; pushErrorScope(filter: GPUErrorFilter): undefined; popErrorScope(): Promise; - onuncapturederror: - | ((this: GPUDevice, ev: GPUUncapturedErrorEvent) => any) - | null; readonly features: GPUSupportedFeatures; readonly limits: GPUSupportedLimits; @@ -189,6 +187,10 @@ declare class GPUDevice extends EventTarget implements GPUObjectBase { declare class GPUBuffer implements GPUObjectBase { label: string; + readonly size: number; + readonly usage: GPUBufferUsageFlags; + readonly mapState: GPUBufferMapState; + mapAsync( mode: GPUMapModeFlags, offset?: number, @@ -200,6 +202,9 @@ declare class GPUBuffer implements GPUObjectBase { destroy(): undefined; } +/** @category WebGPU */ +declare type GPUBufferMapState = "unmapped" | "pending" | "mapped"; + /** @category WebGPU */ declare interface GPUBufferDescriptor extends GPUObjectDescriptorBase { size: number; @@ -239,6 +244,15 @@ declare class GPUTexture implements GPUObjectBase { createView(descriptor?: GPUTextureViewDescriptor): GPUTextureView; destroy(): undefined; + + readonly width: number; + readonly height: number; + readonly depthOrArrayLayers: number; + readonly mipLevelCount: number; + readonly sampleCount: number; + readonly dimension: GPUTextureDimension; + readonly format: GPUTextureFormat; + readonly usage: GPUTextureUsageFlags; } /** @category WebGPU */ @@ -249,6 +263,7 @@ declare interface GPUTextureDescriptor extends GPUObjectDescriptorBase { dimension?: GPUTextureDimension; format: GPUTextureFormat; usage: GPUTextureUsageFlags; + viewFormats?: GPUTextureFormat[]; } /** @category WebGPU */ @@ -337,7 +352,6 @@ declare type GPUTextureFormat = | "depth24plus" | "depth24plus-stencil8" | "depth32float" - | "depth24unorm-stencil8" | "depth32float-stencil8" | "bc1-rgba-unorm" | "bc1-rgba-unorm-srgb" @@ -814,6 +828,13 @@ declare interface GPUVertexAttribute { shaderLocation: number; } +/** @category WebGPU */ +declare interface GPUImageDataLayout { + offset?: number; + bytesPerRow?: number; + rowsPerImage?: number; +} + /** @category WebGPU */ declare class GPUCommandBuffer implements GPUObjectBase { label: string; @@ -883,13 +904,6 @@ declare class GPUCommandEncoder implements GPUObjectBase { /** @category WebGPU */ declare interface GPUCommandEncoderDescriptor extends GPUObjectDescriptorBase {} -/** @category WebGPU */ -declare interface GPUImageDataLayout { - offset?: number; - bytesPerRow?: number; - rowsPerImage?: number; -} - /** @category WebGPU */ declare interface GPUImageCopyBuffer extends GPUImageDataLayout { buffer: GPUBuffer; @@ -1091,7 +1105,6 @@ declare class GPURenderPassEncoder declare interface GPURenderPassDescriptor extends GPUObjectDescriptorBase { colorAttachments: (GPURenderPassColorAttachment | null)[]; depthStencilAttachment?: GPURenderPassDepthStencilAttachment; - occlusionQuerySet?: GPUQuerySet; } /** @category WebGPU */ @@ -1229,6 +1242,9 @@ declare class GPUQuerySet implements GPUObjectBase { label: string; destroy(): undefined; + + readonly type: GPUQueryType; + readonly count: number; } /** @category WebGPU */ @@ -1263,9 +1279,6 @@ declare class GPUError { readonly message: string; } -/** @category WebGPU */ -declare type GPUErrorFilter = "out-of-memory" | "validation"; - /** @category WebGPU */ declare class GPUOutOfMemoryError extends GPUError { constructor(message: string); @@ -1277,18 +1290,7 @@ declare class GPUValidationError extends GPUError { } /** @category WebGPU */ -declare class GPUUncapturedErrorEvent extends Event { - constructor( - type: string, - gpuUncapturedErrorEventInitDict: GPUUncapturedErrorEventInit, - ); - readonly error: GPUError; -} - -/** @category WebGPU */ -declare interface GPUUncapturedErrorEventInit extends EventInit { - error?: GPUError; -} +declare type GPUErrorFilter = "out-of-memory" | "validation"; /** @category WebGPU */ declare interface GPUColorDict { diff --git a/ext/webgpu/Cargo.toml b/ext/webgpu/Cargo.toml index 3e1d579f9f..d22eb19e77 100644 --- a/ext/webgpu/Cargo.toml +++ b/ext/webgpu/Cargo.toml @@ -3,16 +3,38 @@ [package] name = "deno_webgpu" version = "0.87.0" -authors.workspace = true +authors = ["the Deno authors"] edition.workspace = true -license.workspace = true +license = "MIT" readme = "README.md" repository = "https://github.com/gfx-rs/wgpu" description = "WebGPU implementation for Deno" +[features] +surface = ["wgpu-core/raw-window-handle", "dep:raw-window-handle"] + [dependencies] deno_core.workspace = true +raw-window-handle = { workspace = true, optional = true } serde.workspace = true tokio.workspace = true -wgpu-core = { version = "0.13", features = ["trace", "replay", "serde"] } -wgpu-types = { version = "0.13", features = ["trace", "replay", "serde"] } +wgpu-types = { workspace = true, features = ["trace", "replay", "serde"] } + +[dependencies.wgpu-core] +workspace = true +features = ["trace", "replay", "serde", "strict_asserts", "wgsl", "gles"] + +# We want the wgpu-core Metal backend on macOS and iOS. +[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies.wgpu-core] +workspace = true +features = ["metal"] + +# We want the wgpu-core Direct3D backends on Windows. +[target.'cfg(windows)'.dependencies.wgpu-core] +workspace = true +features = ["dx11", "dx12"] + +# We want the wgpu-core Vulkan backend on Unix (but not Emscripten) and Windows. +[target.'cfg(any(windows, all(unix, not(target_arch = "emscripten"))))'.dependencies.wgpu-core] +workspace = true +features = ["vulkan"] diff --git a/ext/webgpu/src/01_webgpu.js b/ext/webgpu/src/01_webgpu.js index eb239bab8b..23e8b24f08 100644 --- a/ext/webgpu/src/01_webgpu.js +++ b/ext/webgpu/src/01_webgpu.js @@ -57,7 +57,6 @@ const _architecture = Symbol("[[architecture]]"); const _description = Symbol("[[description]]"); const _limits = Symbol("[[limits]]"); - const _features = Symbol("[[features]]"); const _reason = Symbol("[[reason]]"); const _message = Symbol("[[message]]"); const _label = Symbol("[[label]]"); @@ -68,6 +67,15 @@ const _encoders = Symbol("[[encoders]]"); const _encoder = Symbol("[[encoder]]"); const _descriptor = Symbol("[[descriptor]]"); + const _width = Symbol("[[width]]"); + const _height = Symbol("[[height]]"); + const _depthOrArrayLayers = Symbol("[[depthOrArrayLayers]]"); + const _mipLevelCount = Symbol("[[mipLevelCount]]"); + const _sampleCount = Symbol("[[sampleCount]]"); + const _dimension = Symbol("[[dimension]]"); + const _format = Symbol("[[format]]"); + const _type = Symbol("[[type]]"); + const _count = Symbol("[[count]]"); /** * @param {any} self @@ -175,10 +183,13 @@ } } + const illegalConstructorKey = Symbol("illegalConstructorKey"); class GPUError extends Error { - constructor() { + constructor(key = null) { super(); - webidl.illegalConstructor(); + if (key !== illegalConstructorKey) { + webidl.illegalConstructor(); + } } [_message]; @@ -189,20 +200,6 @@ } const GPUErrorPrototype = GPUError.prototype; - class GPUOutOfMemoryError extends GPUError { - name = "GPUOutOfMemoryError"; - constructor(message) { - const prefix = "Failed to construct 'GPUOutOfMemoryError'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - message = webidl.converters.DOMString(message, { - prefix, - context: "Argument 1", - }); - super(message); - } - } - const GPUOutOfMemoryErrorPrototype = GPUOutOfMemoryError.prototype; - class GPUValidationError extends GPUError { name = "GPUValidationError"; /** @param {string} message */ @@ -213,11 +210,29 @@ prefix, context: "Argument 1", }); - super(message); + super(illegalConstructorKey); + this[webidl.brand] = webidl.brand; + this[_message] = message; } } const GPUValidationErrorPrototype = GPUValidationError.prototype; + class GPUOutOfMemoryError extends GPUError { + name = "GPUOutOfMemoryError"; + constructor(message) { + const prefix = "Failed to construct 'GPUOutOfMemoryError'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + message = webidl.converters.DOMString(message, { + prefix, + context: "Argument 1", + }); + super(illegalConstructorKey); + this[webidl.brand] = webidl.brand; + this[_message] = message; + } + } + const GPUOutOfMemoryErrorPrototype = GPUOutOfMemoryError.prototype; + class GPU { [webidl.brand] = webidl.brand; @@ -314,27 +329,24 @@ const requiredFeatures = descriptor.requiredFeatures ?? []; for (let i = 0; i < requiredFeatures.length; ++i) { const feature = requiredFeatures[i]; - if (!SetPrototypeHas(this[_adapter].features[_features], feature)) { + if ( + !SetPrototypeHas( + this[_adapter].features[webidl.setlikeInner], + feature, + ) + ) { throw new TypeError( - `${prefix}: nonGuaranteedFeatures must be a subset of the adapter features.`, + `${prefix}: requiredFeatures must be a subset of the adapter features.`, ); } } - let requiredLimits = descriptor.requiredLimits; - if (requiredLimits) { - requiredLimits = { - ...this[_adapter].limits[_limits], - ...requiredLimits, - }; - } - // TODO(lucacasonato): validate requiredLimits const { rid, features, limits } = await core.opAsync( "op_webgpu_request_device", this[_adapter].rid, descriptor.label, requiredFeatures, - requiredLimits, + descriptor.requiredLimits, ); const inner = new InnerGPUDevice({ @@ -344,9 +356,9 @@ limits: createGPUSupportedLimits(limits), }); return createGPUDevice( - descriptor.label ?? null, + descriptor.label, inner, - createGPUQueue(descriptor.label ?? null, inner), + createGPUQueue(descriptor.label, inner), ); } @@ -441,10 +453,10 @@ } const GPUAdapterInfoPrototype = GPUAdapterInfo.prototype; - function createGPUSupportedLimits(features) { + function createGPUSupportedLimits(limits) { /** @type {GPUSupportedLimits} */ const adapterFeatures = webidl.createBranded(GPUSupportedLimits); - adapterFeatures[_limits] = features; + adapterFeatures[_limits] = limits; return adapterFeatures; } @@ -505,6 +517,14 @@ webidl.assertBranded(this, GPUSupportedLimitsPrototype); return this[_limits].maxBindGroups; } + get maxBindingsPerBindGroup() { + webidl.assertBranded(this, GPUSupportedLimitsPrototype); + return this[_limits].maxBindingsPerBindGroup; + } + get maxBufferSize() { + webidl.assertBranded(this, GPUSupportedLimitsPrototype); + return this[_limits].maxBufferSize; + } get maxDynamicUniformBuffersPerPipelineLayout() { webidl.assertBranded(this, GPUSupportedLimitsPrototype); return this[_limits].maxDynamicUniformBuffersPerPipelineLayout; @@ -611,7 +631,6 @@ constructor() { webidl.illegalConstructor(); } - [SymbolFor("Deno.privateCustomInspect")](inspect) { return `${this.constructor.name} ${ inspect([...new SafeArrayIterator(this.values())]) @@ -923,7 +942,7 @@ }; } const buffer = createGPUBuffer( - descriptor.label ?? null, + descriptor.label, device, rid, descriptor.size, @@ -955,7 +974,7 @@ device.pushError(err); const texture = createGPUTexture( - descriptor.label ?? null, + descriptor, device, rid, ); @@ -975,14 +994,14 @@ context: "Argument 1", }); const device = assertDevice(this, { prefix, context: "this" }); - const { rid, err } = ops.op_webgpu_create_sampler({ + const { rid, err } = ops.op_webgpu_create_texture({ deviceRid: device.rid, ...descriptor, }); device.pushError(err); const sampler = createGPUSampler( - descriptor.label ?? null, + descriptor.label, device, rid, ); @@ -1006,13 +1025,13 @@ for (let i = 0; i < descriptor.entries.length; ++i) { const entry = descriptor.entries[i]; - let count = 0; - if (entry.buffer) count++; - if (entry.sampler) count++; - if (entry.texture) count++; - if (entry.storageTexture) count++; + let j = 0; + if (entry.buffer) j++; + if (entry.sampler) j++; + if (entry.texture) j++; + if (entry.storageTexture) j++; - if (count !== 1) { + if (j !== 1) { throw new Error(); // TODO(@crowlKats): correct error } } @@ -1025,7 +1044,7 @@ device.pushError(err); const bindGroupLayout = createGPUBindGroupLayout( - descriptor.label ?? null, + descriptor.label, device, rid, ); @@ -1067,7 +1086,7 @@ device.pushError(err); const pipelineLayout = createGPUPipelineLayout( - descriptor.label ?? null, + descriptor.label, device, rid, ); @@ -1162,7 +1181,7 @@ device.pushError(err); const bindGroup = createGPUBindGroup( - descriptor.label ?? null, + descriptor.label, device, rid, ); @@ -1186,12 +1205,11 @@ device.rid, descriptor.label, descriptor.code, - descriptor.sourceMap, ); device.pushError(err); const shaderModule = createGPUShaderModule( - descriptor.label ?? null, + descriptor.label, device, rid, ); @@ -1245,7 +1263,7 @@ device.pushError(err); const computePipeline = createGPUComputePipeline( - descriptor.label ?? null, + descriptor.label, device, rid, ); @@ -1320,7 +1338,7 @@ device.pushError(err); const renderPipeline = createGPURenderPipeline( - descriptor.label ?? null, + descriptor.label, device, rid, ); @@ -1357,7 +1375,7 @@ device.pushError(err); const commandEncoder = createGPUCommandEncoder( - descriptor.label ?? null, + descriptor.label, device, rid, ); @@ -1382,16 +1400,14 @@ }, ); const device = assertDevice(this, { prefix, context: "this" }); - const { rid, err } = ops.op_webgpu_create_render_bundle_encoder( - { - deviceRid: device.rid, - ...descriptor, - }, - ); + const { rid, err } = ops.op_webgpu_create_render_bundle_encoder({ + deviceRid: device.rid, + ...descriptor, + }); device.pushError(err); const renderBundleEncoder = createGPURenderBundleEncoder( - descriptor.label ?? null, + descriptor.label, device, rid, ); @@ -1422,7 +1438,7 @@ device.pushError(err); const querySet = createGPUQuerySet( - descriptor.label ?? null, + descriptor.label, device, rid, descriptor, @@ -1523,7 +1539,7 @@ * @param {GPUCommandBuffer[]} commandBuffers */ submit(commandBuffers) { - webidl.assertBranded(this, GPUQueue.prototype); + webidl.assertBranded(this, GPUQueuePrototype); const prefix = "Failed to execute 'submit' on 'GPUQueue'"; webidl.requiredArguments(arguments.length, 1, { prefix, @@ -1546,10 +1562,7 @@ return rid; }, ); - const { err } = ops.op_webgpu_queue_submit( - device.rid, - commandBufferRids, - ); + const { err } = ops.op_webgpu_queue_submit(device.rid, commandBufferRids); for (let i = 0; i < commandBuffers.length; ++i) { commandBuffers[i][_rid] = undefined; } @@ -1557,7 +1570,7 @@ } onSubmittedWorkDone() { - webidl.assertBranded(this, GPUQueue.prototype); + webidl.assertBranded(this, GPUQueuePrototype); return PromiseResolve(); } @@ -1569,7 +1582,7 @@ * @param {number} [size] */ writeBuffer(buffer, bufferOffset, data, dataOffset = 0, size) { - webidl.assertBranded(this, GPUQueue.prototype); + webidl.assertBranded(this, GPUQueuePrototype); const prefix = "Failed to execute 'writeBuffer' on 'GPUQueue'"; webidl.requiredArguments(arguments.length, 3, { prefix }); buffer = webidl.converters["GPUBuffer"](buffer, { @@ -1622,7 +1635,7 @@ * @param {GPUExtent3D} size */ writeTexture(destination, data, dataLayout, size) { - webidl.assertBranded(this, GPUQueue.prototype); + webidl.assertBranded(this, GPUQueuePrototype); const prefix = "Failed to execute 'writeTexture' on 'GPUQueue'"; webidl.requiredArguments(arguments.length, 4, { prefix }); destination = webidl.converters.GPUImageCopyTexture(destination, { @@ -1681,6 +1694,7 @@ } } GPUObjectBaseMixin("GPUQueue", GPUQueue); + const GPUQueuePrototype = GPUQueue.prototype; /** * @typedef CreateGPUBufferOptions @@ -1716,25 +1730,18 @@ class GPUBuffer { /** @type {InnerGPUDevice} */ [_device]; - /** @type {number} */ [_rid]; - /** @type {number} */ [_size]; - /** @type {number} */ [_usage]; - - /** @type {"mapped" | "mapped at creation" | "mapped pending" | "unmapped" | "destroy"} */ + /** @type {"mapped" | "mapped at creation" | "pending" | "unmapped" | "destroy"} */ [_state]; - /** @type {[number, number] | null} */ [_mappingRange]; - /** @type {[ArrayBuffer, number, number][] | null} */ [_mappedRanges]; - /** @type {number} */ [_mapMode]; @@ -1761,13 +1768,33 @@ webidl.illegalConstructor(); } + get size() { + webidl.assertBranded(this, GPUBufferPrototype); + return this[_size]; + } + + get usage() { + webidl.assertBranded(this, GPUBufferPrototype); + return this[_usage]; + } + + get mapState() { + webidl.assertBranded(this, GPUBufferPrototype); + const state = this[_state]; + if (state === "mapped at creation") { + return "mapped"; + } else { + return state; + } + } + /** * @param {number} mode * @param {number} offset * @param {number} [size] */ async mapAsync(mode, offset = 0, size) { - webidl.assertBranded(this, GPUBuffer.prototype); + webidl.assertBranded(this, GPUBufferPrototype); const prefix = "Failed to execute 'mapAsync' on 'GPUBuffer'"; webidl.requiredArguments(arguments.length, 1, { prefix }); mode = webidl.converters.GPUMapModeFlags(mode, { @@ -1839,7 +1866,7 @@ } this[_mapMode] = mode; - this[_state] = "mapping pending"; + this[_state] = "pending"; const promise = PromisePrototypeThen( core.opAsync( "op_webgpu_buffer_get_map_async", @@ -1867,7 +1894,7 @@ * @param {number} size */ getMappedRange(offset = 0, size) { - webidl.assertBranded(this, GPUBuffer.prototype); + webidl.assertBranded(this, GPUBufferPrototype); const prefix = "Failed to execute 'getMappedRange' on 'GPUBuffer'"; offset = webidl.converters.GPUSize64(offset, { prefix, @@ -1922,7 +1949,7 @@ } unmap() { - webidl.assertBranded(this, GPUBuffer.prototype); + webidl.assertBranded(this, GPUBufferPrototype); const prefix = "Failed to execute 'unmap' on 'GPUBuffer'"; const device = assertDevice(this, { prefix, context: "this" }); const bufferRid = assertResource(this, { prefix, context: "this" }); @@ -1932,7 +1959,7 @@ "OperationError", ); } - if (this[_state] === "mapping pending") { + if (this[_state] === "pending") { // TODO(lucacasonato): this is not spec compliant. throw new DOMException( `${prefix}: can not unmap while mapping. This is a Deno limitation.`, @@ -1980,7 +2007,7 @@ } destroy() { - webidl.assertBranded(this, GPUBuffer.prototype); + webidl.assertBranded(this, GPUBufferPrototype); this[_cleanup](); } @@ -1993,6 +2020,7 @@ } } GPUObjectBaseMixin("GPUBuffer", GPUBuffer); + const GPUBufferPrototype = GPUBuffer.prototype; class GPUBufferUsage { constructor() { @@ -2045,18 +2073,26 @@ } /** - * @param {string | null} label + * @param {GPUTextureDescriptor} descriptor * @param {InnerGPUDevice} device * @param {number} rid * @returns {GPUTexture} */ - function createGPUTexture(label, device, rid) { + function createGPUTexture(descriptor, device, rid) { /** @type {GPUTexture} */ const texture = webidl.createBranded(GPUTexture); - texture[_label] = label; + texture[_label] = descriptor.label; texture[_device] = device; texture[_rid] = rid; texture[_views] = []; + texture[_width] = descriptor.size.width; + texture[_height] = descriptor.size.height; + texture[_depthOrArrayLayers] = descriptor.size.depthOrArrayLayers; + texture[_mipLevelCount] = descriptor.mipLevelCount; + texture[_sampleCount] = descriptor.sampleCount; + texture[_dimension] = descriptor.dimension; + texture[_format] = descriptor.format; + texture[_usage] = descriptor.usage; return texture; } @@ -2068,6 +2104,23 @@ /** @type {WeakRef[]} */ [_views]; + /** @type {number} */ + [_width]; + /** @type {number} */ + [_height]; + /** @type {number} */ + [_depthOrArrayLayers]; + /** @type {number} */ + [_mipLevelCount]; + /** @type {number} */ + [_sampleCount]; + /** @type {GPUTextureDimension} */ + [_dimension]; + /** @type {GPUTextureFormat} */ + [_format]; + /** @type {number} */ + [_usage]; + [_cleanup]() { const views = this[_views]; while (views.length > 0) { @@ -2092,7 +2145,7 @@ * @param {GPUTextureViewDescriptor} descriptor */ createView(descriptor = {}) { - webidl.assertBranded(this, GPUTexture.prototype); + webidl.assertBranded(this, GPUTexturePrototype); const prefix = "Failed to execute 'createView' on 'GPUTexture'"; webidl.requiredArguments(arguments.length, 0, { prefix }); descriptor = webidl.converters.GPUTextureViewDescriptor(descriptor, { @@ -2108,7 +2161,7 @@ device.pushError(err); const textureView = createGPUTextureView( - descriptor.label ?? null, + descriptor.label, this, rid, ); @@ -2117,10 +2170,50 @@ } destroy() { - webidl.assertBranded(this, GPUTexture.prototype); + webidl.assertBranded(this, GPUTexturePrototype); this[_cleanup](); } + get width() { + webidl.assertBranded(this, GPUTexturePrototype); + return this[_width]; + } + + get height() { + webidl.assertBranded(this, GPUTexturePrototype); + return this[_height]; + } + + get depthOrArrayLayers() { + webidl.assertBranded(this, GPUTexturePrototype); + return this[_depthOrArrayLayers]; + } + + get mipLevelCount() { + webidl.assertBranded(this, GPUTexturePrototype); + return this[_mipLevelCount]; + } + + get sampleCount() { + webidl.assertBranded(this, GPUTexturePrototype); + return this[_sampleCount]; + } + + get dimension() { + webidl.assertBranded(this, GPUTexturePrototype); + return this[_dimension]; + } + + get format() { + webidl.assertBranded(this, GPUTexturePrototype); + return this[_format]; + } + + get usage() { + webidl.assertBranded(this, GPUTexturePrototype); + return this[_usage]; + } + [SymbolFor("Deno.privateCustomInspect")](inspect) { return `${this.constructor.name} ${ inspect({ @@ -2130,6 +2223,7 @@ } } GPUObjectBaseMixin("GPUTexture", GPUTexture); + const GPUTexturePrototype = GPUTexture.prototype; class GPUTextureUsage { constructor() { @@ -2763,7 +2857,7 @@ ); const renderPassEncoder = createGPURenderPassEncoder( - descriptor.label ?? null, + descriptor.label, this, rid, ); @@ -2795,7 +2889,7 @@ ); const computePassEncoder = createGPUComputePassEncoder( - descriptor.label ?? null, + descriptor.label, this, rid, ); @@ -3310,7 +3404,7 @@ this[_rid] = undefined; const commandBuffer = createGPUCommandBuffer( - descriptor.label ?? null, + descriptor.label, device, rid, ); @@ -3573,9 +3667,7 @@ context: "encoder referenced by this", }); const renderPassRid = assertResource(this, { prefix, context: "this" }); - ops.op_webgpu_render_pass_end_pipeline_statistics_query( - renderPassRid, - ); + ops.op_webgpu_render_pass_end_pipeline_statistics_query(renderPassRid); } /** @@ -3651,10 +3743,7 @@ }); return rid; }); - ops.op_webgpu_render_pass_execute_bundles( - renderPassRid, - bundleRids, - ); + ops.op_webgpu_render_pass_execute_bundles(renderPassRid, bundleRids); } end() { @@ -3747,10 +3836,7 @@ context: "encoder referenced by this", }); const renderPassRid = assertResource(this, { prefix, context: "this" }); - ops.op_webgpu_render_pass_push_debug_group( - renderPassRid, - groupLabel, - ); + ops.op_webgpu_render_pass_push_debug_group(renderPassRid, groupLabel); } popDebugGroup() { @@ -3790,10 +3876,7 @@ context: "encoder referenced by this", }); const renderPassRid = assertResource(this, { prefix, context: "this" }); - ops.op_webgpu_render_pass_insert_debug_marker( - renderPassRid, - markerLabel, - ); + ops.op_webgpu_render_pass_insert_debug_marker(renderPassRid, markerLabel); } /** @@ -3826,10 +3909,7 @@ resourceContext: "Argument 1", selfContext: "this", }); - ops.op_webgpu_render_pass_set_pipeline( - renderPassRid, - pipelineRid, - ); + ops.op_webgpu_render_pass_set_pipeline(renderPassRid, pipelineRid); } /** @@ -4205,10 +4285,7 @@ resourceContext: "Argument 1", selfContext: "this", }); - ops.op_webgpu_compute_pass_set_pipeline( - computePassRid, - pipelineRid, - ); + ops.op_webgpu_compute_pass_set_pipeline(computePassRid, pipelineRid); } /** @@ -4351,9 +4428,7 @@ context: "encoder referenced by this", }); const computePassRid = assertResource(this, { prefix, context: "this" }); - ops.op_webgpu_compute_pass_end_pipeline_statistics_query( - computePassRid, - ); + ops.op_webgpu_compute_pass_end_pipeline_statistics_query(computePassRid); } /** @@ -4488,10 +4563,7 @@ context: "encoder referenced by this", }); const computePassRid = assertResource(this, { prefix, context: "this" }); - ops.op_webgpu_compute_pass_push_debug_group( - computePassRid, - groupLabel, - ); + ops.op_webgpu_compute_pass_push_debug_group(computePassRid, groupLabel); } popDebugGroup() { @@ -4630,7 +4702,7 @@ * @param {GPURenderBundleDescriptor} descriptor */ finish(descriptor = {}) { - webidl.assertBranded(this, GPURenderBundleEncoder.prototype); + webidl.assertBranded(this, GPURenderBundleEncoderPrototype); const prefix = "Failed to execute 'finish' on 'GPURenderBundleEncoder'"; descriptor = webidl.converters.GPURenderBundleDescriptor(descriptor, { prefix, @@ -4649,7 +4721,7 @@ this[_rid] = undefined; const renderBundle = createGPURenderBundle( - descriptor.label ?? null, + descriptor.label, device, rid, ); @@ -4665,7 +4737,7 @@ dynamicOffsetsDataStart, dynamicOffsetsDataLength, ) { - webidl.assertBranded(this, GPURenderBundleEncoder.prototype); + webidl.assertBranded(this, GPURenderBundleEncoderPrototype); const prefix = "Failed to execute 'setBindGroup' on 'GPURenderBundleEncoder'"; const device = assertDevice(this, { prefix, context: "this" }); @@ -4706,7 +4778,7 @@ * @param {string} groupLabel */ pushDebugGroup(groupLabel) { - webidl.assertBranded(this, GPURenderBundleEncoder.prototype); + webidl.assertBranded(this, GPURenderBundleEncoderPrototype); const prefix = "Failed to execute 'pushDebugGroup' on 'GPURenderBundleEncoder'"; webidl.requiredArguments(arguments.length, 1, { prefix }); @@ -4726,7 +4798,7 @@ } popDebugGroup() { - webidl.assertBranded(this, GPURenderBundleEncoder.prototype); + webidl.assertBranded(this, GPURenderBundleEncoderPrototype); const prefix = "Failed to execute 'popDebugGroup' on 'GPURenderBundleEncoder'"; assertDevice(this, { prefix, context: "this" }); @@ -4743,7 +4815,7 @@ * @param {string} markerLabel */ insertDebugMarker(markerLabel) { - webidl.assertBranded(this, GPURenderBundleEncoder.prototype); + webidl.assertBranded(this, GPURenderBundleEncoderPrototype); const prefix = "Failed to execute 'insertDebugMarker' on 'GPURenderBundleEncoder'"; webidl.requiredArguments(arguments.length, 1, { prefix }); @@ -4766,7 +4838,7 @@ * @param {GPURenderPipeline} pipeline */ setPipeline(pipeline) { - webidl.assertBranded(this, GPURenderBundleEncoder.prototype); + webidl.assertBranded(this, GPURenderBundleEncoderPrototype); const prefix = "Failed to execute 'setPipeline' on 'GPURenderBundleEncoder'"; webidl.requiredArguments(arguments.length, 1, { prefix }); @@ -4801,7 +4873,7 @@ * @param {number} size */ setIndexBuffer(buffer, indexFormat, offset = 0, size = 0) { - webidl.assertBranded(this, GPURenderBundleEncoder.prototype); + webidl.assertBranded(this, GPURenderBundleEncoderPrototype); const prefix = "Failed to execute 'setIndexBuffer' on 'GPURenderBundleEncoder'"; webidl.requiredArguments(arguments.length, 2, { prefix }); @@ -4851,7 +4923,7 @@ * @param {number} size */ setVertexBuffer(slot, buffer, offset = 0, size = 0) { - webidl.assertBranded(this, GPURenderBundleEncoder.prototype); + webidl.assertBranded(this, GPURenderBundleEncoderPrototype); const prefix = "Failed to execute 'setVertexBuffer' on 'GPURenderBundleEncoder'"; webidl.requiredArguments(arguments.length, 2, { prefix }); @@ -4901,7 +4973,7 @@ * @param {number} firstInstance */ draw(vertexCount, instanceCount = 1, firstVertex = 0, firstInstance = 0) { - webidl.assertBranded(this, GPURenderBundleEncoder.prototype); + webidl.assertBranded(this, GPURenderBundleEncoderPrototype); const prefix = "Failed to execute 'draw' on 'GPURenderBundleEncoder'"; webidl.requiredArguments(arguments.length, 1, { prefix }); vertexCount = webidl.converters.GPUSize32(vertexCount, { @@ -4948,7 +5020,7 @@ baseVertex = 0, firstInstance = 0, ) { - webidl.assertBranded(this, GPURenderBundleEncoder.prototype); + webidl.assertBranded(this, GPURenderBundleEncoderPrototype); const prefix = "Failed to execute 'drawIndexed' on 'GPURenderBundleEncoder'"; webidl.requiredArguments(arguments.length, 1, { prefix }); @@ -4992,7 +5064,7 @@ * @param {number} indirectOffset */ drawIndirect(indirectBuffer, indirectOffset) { - webidl.assertBranded(this, GPURenderBundleEncoder.prototype); + webidl.assertBranded(this, GPURenderBundleEncoderPrototype); const prefix = "Failed to execute 'drawIndirect' on 'GPURenderBundleEncoder'"; webidl.requiredArguments(arguments.length, 2, { prefix }); @@ -5038,6 +5110,7 @@ } } GPUObjectBaseMixin("GPURenderBundleEncoder", GPURenderBundleEncoder); + const GPURenderBundleEncoderPrototype = GPURenderBundleEncoder.prototype; /** * @param {string | null} label @@ -5106,6 +5179,10 @@ [_rid]; /** @type {GPUQuerySetDescriptor} */ [_descriptor]; + /** @type {GPUQueryType} */ + [_type]; + /** @type {number} */ + [_count]; [_cleanup]() { const rid = this[_rid]; @@ -5125,6 +5202,16 @@ this[_cleanup](); } + get type() { + webidl.assertBranded(this, GPUQuerySetPrototype); + return this[_type](); + } + + get count() { + webidl.assertBranded(this, GPUQuerySetPrototype); + return this[_count](); + } + [SymbolFor("Deno.privateCustomInspect")](inspect) { return `${this.constructor.name} ${ inspect({ @@ -5137,12 +5224,17 @@ const GPUQuerySetPrototype = GPUQuerySet.prototype; window.__bootstrap.webgpu = { + _device, + assertDevice, + createGPUTexture, gpu: webidl.createBranded(GPU), GPU, GPUAdapter, + GPUAdapterInfo, GPUSupportedLimits, GPUSupportedFeatures, GPUDevice, + GPUDeviceLostInfo, GPUQueue, GPUBuffer, GPUBufferUsage, @@ -5167,7 +5259,7 @@ GPURenderBundle, GPUQuerySet, GPUError, - GPUOutOfMemoryError, GPUValidationError, + GPUOutOfMemoryError, }; })(this); diff --git a/ext/webgpu/src/02_idl_types.js b/ext/webgpu/src/02_idl_types.js index 42986d4fd7..c927f10a5a 100644 --- a/ext/webgpu/src/02_idl_types.js +++ b/ext/webgpu/src/02_idl_types.js @@ -65,12 +65,6 @@ GPUSupportedFeatures.prototype, ); - // ENUM: GPUPredefinedColorSpace - webidl.converters.GPUPredefinedColorSpace = webidl.createEnumConverter( - "GPUPredefinedColorSpace", - ["srgb"], - ); - // INTERFACE: GPU webidl.converters.GPU = webidl.createInterfaceConverter("GPU", GPU.prototype); @@ -112,7 +106,6 @@ "GPUFeatureName", [ "depth-clip-control", - "depth24unorm-stencil8", "depth32float-stencil8", "pipeline-statistics-query", "texture-compression-bc", @@ -148,6 +141,10 @@ webidl.converters["GPUSize32"] = (V, opts) => webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }); + // TYPEDEF: GPUSize64 + webidl.converters["GPUSize64"] = (V, opts) => + webidl.converters["unsigned long long"](V, { ...opts, enforceRange: true }); + // DICTIONARY: GPUDeviceDescriptor const dictMembersGPUDeviceDescriptor = [ { @@ -163,7 +160,7 @@ key: "requiredLimits", converter: webidl.createRecordConverter( webidl.converters["DOMString"], - webidl.converters["GPUSize32"], + webidl.converters["GPUSize64"], ), }, ]; @@ -185,10 +182,6 @@ GPUBuffer.prototype, ); - // TYPEDEF: GPUSize64 - webidl.converters["GPUSize64"] = (V, opts) => - webidl.converters["unsigned long long"](V, { ...opts, enforceRange: true }); - // TYPEDEF: GPUBufferUsageFlags webidl.converters["GPUBufferUsageFlags"] = (V, opts) => webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }); @@ -339,7 +332,6 @@ "depth24plus", "depth24plus-stencil8", "depth32float", - "depth24unorm-stencil8", "depth32float-stencil8", "bc1-rgba-unorm", "bc1-rgba-unorm-srgb", @@ -432,6 +424,15 @@ converter: webidl.converters["GPUTextureUsageFlags"], required: true, }, + { + key: "viewFormats", + converter: webidl.createSequenceConverter( + webidl.converters["GPUTextureFormat"], + ), + get defaultValue() { + return []; + }, + }, ]; webidl.converters["GPUTextureDescriptor"] = webidl.createDictionaryConverter( "GPUTextureDescriptor", @@ -911,7 +912,6 @@ converter: webidl.converters["DOMString"], required: true, }, - { key: "sourceMap", converter: webidl.converters["object"] }, ]; webidl.converters["GPUShaderModuleDescriptor"] = webidl .createDictionaryConverter( @@ -1842,7 +1842,6 @@ key: "depthStencilAttachment", converter: webidl.converters["GPURenderPassDepthStencilAttachment"], }, - { key: "occlusionQuerySet", converter: webidl.converters["GPUQuerySet"] }, ]; webidl.converters["GPURenderPassDescriptor"] = webidl .createDictionaryConverter( diff --git a/ext/webgpu/src/03_surface.js b/ext/webgpu/src/03_surface.js new file mode 100644 index 0000000000..0152098ead --- /dev/null +++ b/ext/webgpu/src/03_surface.js @@ -0,0 +1,148 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +// @ts-check +/// +/// +/// +/// + +"use strict"; + +((window) => { + const core = window.Deno.core; + const ops = core.ops; + const webidl = window.__bootstrap.webidl; + const { Symbol } = window.__bootstrap.primordials; + const { _device, assertDevice, createGPUTexture } = window.__bootstrap.webgpu; + + const _surfaceRid = Symbol("[[surfaceRid]]"); + const _configuration = Symbol("[[configuration]]"); + const _canvas = Symbol("[[canvas]]"); + const _currentTexture = Symbol("[[currentTexture]]"); + class GPUCanvasContext { + /** @type {number} */ + [_surfaceRid]; + /** @type {InnerGPUDevice} */ + [_device]; + [_configuration]; + [_canvas]; + /** @type {GPUTexture | undefined} */ + [_currentTexture]; + + get canvas() { + webidl.assertBranded(this, GPUCanvasContextPrototype); + return this[_canvas]; + } + + constructor() { + webidl.illegalConstructor(); + } + + configure(configuration) { + webidl.assertBranded(this, GPUCanvasContextPrototype); + const prefix = "Failed to execute 'configure' on 'GPUCanvasContext'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + configuration = webidl.converters.GPUCanvasConfiguration(configuration, { + prefix, + context: "Argument 1", + }); + + this[_device] = configuration.device[_device]; + this[_configuration] = configuration; + const device = assertDevice(this, { + prefix, + context: "configuration.device", + }); + + const { err } = ops.op_webgpu_surface_configure({ + surfaceRid: this[_surfaceRid], + deviceRid: device.rid, + format: configuration.format, + usage: configuration.usage, + width: configuration.width, + height: configuration.height, + alphaMode: configuration.alphaMode, + }); + + device.pushError(err); + } + + unconfigure() { + webidl.assertBranded(this, GPUCanvasContextPrototype); + + this[_configuration] = null; + this[_device] = null; + } + + getCurrentTexture() { + webidl.assertBranded(this, GPUCanvasContextPrototype); + const prefix = + "Failed to execute 'getCurrentTexture' on 'GPUCanvasContext'"; + + if (this[_configuration] === null) { + throw new DOMException( + "context is not configured.", + "InvalidStateError", + ); + } + + const device = assertDevice(this, { prefix, context: "this" }); + + if (this[_currentTexture]) { + return this[_currentTexture]; + } + + const { rid } = ops.op_webgpu_surface_get_current_texture( + device.rid, + this[_surfaceRid], + ); + + const texture = createGPUTexture( + { + size: { + width: this[_configuration].width, + height: this[_configuration].height, + depthOrArrayLayers: 1, + }, + mipLevelCount: 1, + sampleCount: 1, + dimension: "2d", + format: this[_configuration].format, + usage: this[_configuration].usage, + }, + device, + rid, + ); + device.trackResource(texture); + this[_currentTexture] = texture; + return texture; + } + + // Extended from spec. Required to present the texture; browser don't need this. + present() { + webidl.assertBranded(this, GPUCanvasContextPrototype); + const prefix = "Failed to execute 'present' on 'GPUCanvasContext'"; + const device = assertDevice(this[_currentTexture], { + prefix, + context: "this", + }); + ops.op_webgpu_surface_present(device.rid, this[_surfaceRid]); + this[_currentTexture].destroy(); + this[_currentTexture] = undefined; + } + } + const GPUCanvasContextPrototype = GPUCanvasContext.prototype; + + function createCanvasContext(options) { + const canvasContext = webidl.createBranded(GPUCanvasContext); + canvasContext[_surfaceRid] = options.surfaceRid; + canvasContext[_canvas] = options.canvas; + return canvasContext; + } + + window.__bootstrap.webgpu = { + ...window.__bootstrap.webgpu, + GPUCanvasContext, + createCanvasContext, + }; +})(this); diff --git a/ext/webgpu/src/04_surface_idl_types.js b/ext/webgpu/src/04_surface_idl_types.js new file mode 100644 index 0000000000..9dcfa767ec --- /dev/null +++ b/ext/webgpu/src/04_surface_idl_types.js @@ -0,0 +1,86 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +// @ts-check +/// +/// +/// +/// + +"use strict"; + +((window) => { + const webidl = window.__bootstrap.webidl; + const { GPUTextureUsage } = window.__bootstrap.webgpu; + + // 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, + ); +})(this); diff --git a/ext/webgpu/src/binding.rs b/ext/webgpu/src/binding.rs index 7a35db5031..4c4a864fdd 100644 --- a/ext/webgpu/src/binding.rs +++ b/ext/webgpu/src/binding.rs @@ -199,7 +199,7 @@ pub fn op_webgpu_create_bind_group_layout( gfx_put!(device => instance.device_create_bind_group_layout( device, &descriptor, - std::marker::PhantomData + () ) => state, WebGpuBindGroupLayout) } @@ -234,7 +234,7 @@ pub fn op_webgpu_create_pipeline_layout( gfx_put!(device => instance.device_create_pipeline_layout( device, &descriptor, - std::marker::PhantomData + () ) => state, super::pipeline::WebGpuPipelineLayout) } @@ -317,6 +317,6 @@ pub fn op_webgpu_create_bind_group( gfx_put!(device => instance.device_create_bind_group( device, &descriptor, - std::marker::PhantomData + () ) => state, WebGpuBindGroup) } diff --git a/ext/webgpu/src/buffer.rs b/ext/webgpu/src/buffer.rs index a10642f33a..58348129eb 100644 --- a/ext/webgpu/src/buffer.rs +++ b/ext/webgpu/src/buffer.rs @@ -13,6 +13,7 @@ use std::cell::RefCell; use std::convert::TryFrom; use std::rc::Rc; use std::time::Duration; +use wgpu_core::resource::BufferAccessResult; use super::error::DomExceptionOperationError; use super::error::WebGpuResult; @@ -57,7 +58,7 @@ pub fn op_webgpu_create_buffer( gfx_put!(device => instance.device_create_buffer( device, &descriptor, - std::marker::PhantomData + () ) => state, WebGpuBuffer) } @@ -70,7 +71,7 @@ pub async fn op_webgpu_buffer_get_map_async( offset: u64, size: u64, ) -> Result { - let (sender, receiver) = oneshot::channel::>(); + let (sender, receiver) = oneshot::channel::(); let device; { @@ -85,12 +86,7 @@ pub async fn op_webgpu_buffer_get_map_async( device = device_resource.0; let callback = Box::new(move |status| { - sender - .send(match status { - wgpu_core::resource::BufferMapAsyncStatus::Success => Ok(()), - _ => unreachable!(), // TODO - }) - .unwrap(); + sender.send(status).unwrap(); }); // TODO(lucacasonato): error handling @@ -120,7 +116,8 @@ pub async fn op_webgpu_buffer_get_map_async( { let state = state.borrow(); let instance = state.borrow::(); - gfx_select!(device => instance.device_poll(device, wgpu_types::Maintain::Wait)).unwrap(); + gfx_select!(device => instance.device_poll(device, wgpu_types::Maintain::Wait)) + .unwrap(); } tokio::time::sleep(Duration::from_millis(10)).await; } diff --git a/ext/webgpu/src/bundle.rs b/ext/webgpu/src/bundle.rs index 13b0d0cc70..3d0f11d896 100644 --- a/ext/webgpu/src/bundle.rs +++ b/ext/webgpu/src/bundle.rs @@ -1,5 +1,6 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +use deno_core::error::type_error; use deno_core::error::AnyError; use deno_core::op; use deno_core::OpState; @@ -108,7 +109,7 @@ pub fn op_webgpu_render_bundle_encoder_finish( &wgpu_core::command::RenderBundleDescriptor { label: label.map(Cow::from), }, - std::marker::PhantomData + () ) => state, WebGpuRenderBundle) } @@ -134,8 +135,8 @@ pub fn op_webgpu_render_bundle_encoder_set_bind_group( // Align the data assert!(dynamic_offsets_data.len() % std::mem::size_of::() == 0); let (prefix, dynamic_offsets_data, suffix) = - // SAFETY: A u8 to u32 cast is safe because we asserted that the length is a - // multiple of 4. + // SAFETY: A u8 to u32 cast is safe because we asserted that the length is a + // multiple of 4. unsafe { dynamic_offsets_data.align_to::() }; assert!(prefix.is_empty()); assert!(suffix.is_empty()); @@ -268,16 +269,15 @@ pub fn op_webgpu_render_bundle_encoder_set_index_buffer( state .resource_table .get::(render_bundle_encoder_rid)?; + let size = Some( + std::num::NonZeroU64::new(size) + .ok_or_else(|| type_error("size must be larger than 0"))?, + ); render_bundle_encoder_resource .0 .borrow_mut() - .set_index_buffer( - buffer_resource.0, - index_format, - offset, - std::num::NonZeroU64::new(size), - ); + .set_index_buffer(buffer_resource.0, index_format, offset, size); Ok(WebGpuResult::empty()) } @@ -298,13 +298,17 @@ pub fn op_webgpu_render_bundle_encoder_set_vertex_buffer( state .resource_table .get::(render_bundle_encoder_rid)?; + let size = Some( + std::num::NonZeroU64::new(size) + .ok_or_else(|| type_error("size must be larger than 0"))?, + ); wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_vertex_buffer( &mut render_bundle_encoder_resource.0.borrow_mut(), slot, buffer_resource.0, offset, - std::num::NonZeroU64::new(size), + size, ); Ok(WebGpuResult::empty()) diff --git a/ext/webgpu/src/command_encoder.rs b/ext/webgpu/src/command_encoder.rs index e552a14e0a..eb534f6bce 100644 --- a/ext/webgpu/src/command_encoder.rs +++ b/ext/webgpu/src/command_encoder.rs @@ -49,7 +49,7 @@ pub fn op_webgpu_create_command_encoder( gfx_put!(device => instance.device_create_command_encoder( device, &descriptor, - std::marker::PhantomData + () ) => state, WebGpuCommandEncoder) } @@ -84,7 +84,6 @@ pub fn op_webgpu_command_encoder_begin_render_pass( label: Option, color_attachments: Vec>, depth_stencil_attachment: Option, - _occlusion_query_set: Option, // not yet implemented ) -> Result { let command_encoder_resource = state .resource_table diff --git a/ext/webgpu/src/compute_pass.rs b/ext/webgpu/src/compute_pass.rs index 71f9dfe3bd..8132b450c5 100644 --- a/ext/webgpu/src/compute_pass.rs +++ b/ext/webgpu/src/compute_pass.rs @@ -194,8 +194,8 @@ pub fn op_webgpu_compute_pass_set_bind_group( // Align the data assert!(dynamic_offsets_data_start % std::mem::size_of::() == 0); let (prefix, dynamic_offsets_data, suffix) = - // SAFETY: A u8 to u32 cast is safe because we asserted that the length is a - // multiple of 4. + // SAFETY: A u8 to u32 cast is safe because we asserted that the length is a + // multiple of 4. unsafe { dynamic_offsets_data.align_to::() }; assert!(prefix.is_empty()); assert!(suffix.is_empty()); diff --git a/ext/webgpu/src/error.rs b/ext/webgpu/src/error.rs index 55ef223a1c..41d7d6cf34 100644 --- a/ext/webgpu/src/error.rs +++ b/ext/webgpu/src/error.rs @@ -5,7 +5,6 @@ use serde::Serialize; use std::convert::From; use std::error::Error; use std::fmt; -use std::fmt::Write; use wgpu_core::binding_model::CreateBindGroupError; use wgpu_core::binding_model::CreateBindGroupLayoutError; use wgpu_core::binding_model::CreatePipelineLayoutError; @@ -24,6 +23,8 @@ 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; use wgpu_core::resource::CreateQuerySetError; @@ -36,9 +37,7 @@ fn fmt_err(err: &(dyn Error + 'static)) -> String { let mut e = err.source(); while let Some(source) = e { - // No error possible, unwrap is fine here. - // https://github.com/rust-lang/rust/blob/1.47.0/library/alloc/src/string.rs#L2414-L2427 - write!(output, ": {source}").unwrap(); + output.push_str(&format!(": {source}")); e = source.source(); } @@ -281,6 +280,13 @@ impl From for WebGpuError { } } +#[cfg(feature = "surface")] +impl From for WebGpuError { + fn from(err: ConfigureSurfaceError) -> Self { + WebGpuError::Validation(fmt_err(&err)) + } +} + #[derive(Debug)] pub struct DomExceptionOperationError { pub msg: String, diff --git a/ext/webgpu/src/lib.rs b/ext/webgpu/src/lib.rs index d1fb55dbf6..d4fd5660ab 100644 --- a/ext/webgpu/src/lib.rs +++ b/ext/webgpu/src/lib.rs @@ -1,5 +1,7 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +#![warn(unsafe_op_in_unsafe_fn)] + use deno_core::error::AnyError; use deno_core::include_js_files; use deno_core::op; @@ -25,13 +27,22 @@ mod macros { macro_rules! gfx_select { ($id:expr => $global:ident.$method:ident( $($param:expr),* )) => { match $id.backend() { - #[cfg(not(target_os = "macos"))] + #[cfg(any( + all(not(target_arch = "wasm32"), not(target_os = "ios"), not(target_os = "macos")), + feature = "vulkan-portability" + ))] wgpu_types::Backend::Vulkan => $global.$method::( $($param),* ), - #[cfg(target_os = "macos")] + #[cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))] wgpu_types::Backend::Metal => $global.$method::( $($param),* ), - #[cfg(windows)] + #[cfg(all(not(target_arch = "wasm32"), windows))] wgpu_types::Backend::Dx12 => $global.$method::( $($param),* ), - #[cfg(all(unix, not(target_os = "macos")))] + #[cfg(all(not(target_arch = "wasm32"), windows))] + wgpu_types::Backend::Dx11 => $global.$method::( $($param),* ), + #[cfg(any( + all(unix, not(target_os = "macos"), not(target_os = "ios")), + feature = "angle", + target_arch = "wasm32" + ))] wgpu_types::Backend::Gl => $global.$method::( $($param),+ ), other => panic!("Unexpected backend {:?}", other), } @@ -65,6 +76,8 @@ pub mod queue; pub mod render_pass; pub mod sampler; pub mod shader; +#[cfg(feature = "surface")] +pub mod surface; pub mod texture; pub struct Unstable(pub bool); @@ -79,7 +92,8 @@ fn check_unstable(state: &OpState, api_name: &str) { } } -type Instance = wgpu_core::hub::Global; +pub type Instance = + wgpu_core::hub::Global; struct WebGpuAdapter(wgpu_core::id::AdapterId); impl Resource for WebGpuAdapter { @@ -128,9 +142,6 @@ fn deserialize_features(features: &wgpu_types::Features) -> Vec<&'static str> { if features.contains(wgpu_types::Features::DEPTH_CLIP_CONTROL) { return_features.push("depth-clip-control"); } - if features.contains(wgpu_types::Features::DEPTH24UNORM_STENCIL8) { - return_features.push("depth24unorm-stencil8"); - } if features.contains(wgpu_types::Features::DEPTH32FLOAT_STENCIL8) { return_features.push("depth32float-stencil8"); } @@ -243,7 +254,10 @@ pub async fn op_webgpu_request_adapter( state.put(wgpu_core::hub::Global::new( "webgpu", wgpu_core::hub::IdentityManagerFactory, - backends, + wgpu_types::InstanceDescriptor { + backends, + dx12_shader_compiler: wgpu_types::Dx12Compiler::Fxc, + }, )); state.borrow::() }; @@ -255,9 +269,7 @@ pub async fn op_webgpu_request_adapter( }; let res = instance.request_adapter( &descriptor, - wgpu_core::instance::AdapterInputs::Mask(backends, |_| { - std::marker::PhantomData - }), + wgpu_core::instance::AdapterInputs::Mask(backends, |_| ()), ); let adapter = match res { @@ -294,10 +306,6 @@ impl From for wgpu_types::Features { wgpu_types::Features::DEPTH_CLIP_CONTROL, required_features.0.contains("depth-clip-control"), ); - features.set( - wgpu_types::Features::DEPTH24UNORM_STENCIL8, - required_features.0.contains("depth24unorm-stencil8"), - ); features.set( wgpu_types::Features::DEPTH32FLOAT_STENCIL8, required_features.0.contains("depth32float-stencil8"), @@ -427,7 +435,7 @@ pub async fn op_webgpu_request_device( adapter, &descriptor, std::env::var("DENO_WEBGPU_TRACE").ok().as_ref().map(std::path::Path::new), - std::marker::PhantomData + () )); if let Some(err) = maybe_err { return Err(DomExceptionOperationError::new(&err.to_string()).into()); @@ -553,7 +561,7 @@ pub fn op_webgpu_create_query_set( gfx_put!(device => instance.device_create_query_set( device, &descriptor, - std::marker::PhantomData + () ) => state, WebGpuQuerySet) } diff --git a/ext/webgpu/src/pipeline.rs b/ext/webgpu/src/pipeline.rs index ba90eedf0a..2e728d8d94 100644 --- a/ext/webgpu/src/pipeline.rs +++ b/ext/webgpu/src/pipeline.rs @@ -102,8 +102,8 @@ pub fn op_webgpu_create_compute_pipeline( GPUPipelineLayoutOrGPUAutoLayoutMode::Layout(_) => None, GPUPipelineLayoutOrGPUAutoLayoutMode::Auto(GPUAutoLayoutMode::Auto) => { Some(wgpu_core::device::ImplicitPipelineIds { - root_id: std::marker::PhantomData, - group_ids: &[std::marker::PhantomData; MAX_BIND_GROUPS], + root_id: (), + group_ids: &[(); MAX_BIND_GROUPS], }) } }; @@ -111,7 +111,7 @@ pub fn op_webgpu_create_compute_pipeline( let (compute_pipeline, maybe_err) = gfx_select!(device => instance.device_create_compute_pipeline( device, &descriptor, - std::marker::PhantomData, + (), implicit_pipelines )); @@ -142,7 +142,7 @@ pub fn op_webgpu_compute_pipeline_get_bind_group_layout( .get::(compute_pipeline_rid)?; let compute_pipeline = compute_pipeline_resource.0; - let (bind_group_layout, maybe_err) = gfx_select!(compute_pipeline => instance.compute_pipeline_get_bind_group_layout(compute_pipeline, index, std::marker::PhantomData)); + let (bind_group_layout, maybe_err) = gfx_select!(compute_pipeline => instance.compute_pipeline_get_bind_group_layout(compute_pipeline, index, ())); let label = gfx_select!(bind_group_layout => instance.bind_group_layout_label(bind_group_layout)); @@ -376,8 +376,8 @@ pub fn op_webgpu_create_render_pipeline( GPUPipelineLayoutOrGPUAutoLayoutMode::Layout(_) => None, GPUPipelineLayoutOrGPUAutoLayoutMode::Auto(GPUAutoLayoutMode::Auto) => { Some(wgpu_core::device::ImplicitPipelineIds { - root_id: std::marker::PhantomData, - group_ids: &[std::marker::PhantomData; MAX_BIND_GROUPS], + root_id: (), + group_ids: &[(); MAX_BIND_GROUPS], }) } }; @@ -385,7 +385,7 @@ pub fn op_webgpu_create_render_pipeline( let (render_pipeline, maybe_err) = gfx_select!(device => instance.device_create_render_pipeline( device, &descriptor, - std::marker::PhantomData, + (), implicit_pipelines )); @@ -408,7 +408,7 @@ pub fn op_webgpu_render_pipeline_get_bind_group_layout( .get::(render_pipeline_rid)?; let render_pipeline = render_pipeline_resource.0; - let (bind_group_layout, maybe_err) = gfx_select!(render_pipeline => instance.render_pipeline_get_bind_group_layout(render_pipeline, index, std::marker::PhantomData)); + let (bind_group_layout, maybe_err) = gfx_select!(render_pipeline => instance.render_pipeline_get_bind_group_layout(render_pipeline, index, ())); let label = gfx_select!(bind_group_layout => instance.bind_group_layout_label(bind_group_layout)); diff --git a/ext/webgpu/src/render_pass.rs b/ext/webgpu/src/render_pass.rs index 85440b18d0..ee38091bcd 100644 --- a/ext/webgpu/src/render_pass.rs +++ b/ext/webgpu/src/render_pass.rs @@ -254,8 +254,8 @@ pub fn op_webgpu_render_pass_set_bind_group( // Align the data assert_eq!(dynamic_offsets_data_start % std::mem::size_of::(), 0); let (prefix, dynamic_offsets_data, suffix) = - // SAFETY: A u8 to u32 cast is safe because we asserted that the length is a - // multiple of 4. + // SAFETY: A u8 to u32 cast is safe because we asserted that the length is a + // multiple of 4. unsafe { dynamic_offsets_data.align_to::() }; assert!(prefix.is_empty()); assert!(suffix.is_empty()); diff --git a/ext/webgpu/src/sampler.rs b/ext/webgpu/src/sampler.rs index f2f1037f10..b377f68351 100644 --- a/ext/webgpu/src/sampler.rs +++ b/ext/webgpu/src/sampler.rs @@ -65,6 +65,6 @@ pub fn op_webgpu_create_sampler( gfx_put!(device => instance.device_create_sampler( device, &descriptor, - std::marker::PhantomData + () ) => state, WebGpuSampler) } diff --git a/ext/webgpu/src/shader.rs b/ext/webgpu/src/shader.rs index f45ab4400b..242cb85baa 100644 --- a/ext/webgpu/src/shader.rs +++ b/ext/webgpu/src/shader.rs @@ -22,7 +22,6 @@ pub fn op_webgpu_create_shader_module( device_rid: ResourceId, label: Option, code: String, - _source_map: Option<()>, // not yet implemented ) -> Result { let instance = state.borrow::(); let device_resource = state @@ -41,6 +40,6 @@ pub fn op_webgpu_create_shader_module( device, &descriptor, source, - std::marker::PhantomData + () ) => state, WebGpuShaderModule) } diff --git a/ext/webgpu/src/surface.rs b/ext/webgpu/src/surface.rs new file mode 100644 index 0000000000..1adebb53a4 --- /dev/null +++ b/ext/webgpu/src/surface.rs @@ -0,0 +1,137 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +use super::WebGpuResult; +use deno_core::error::AnyError; +use deno_core::include_js_files; +use deno_core::op; +use deno_core::Extension; +use deno_core::OpState; +use deno_core::Resource; +use deno_core::ResourceId; +use serde::Deserialize; +use std::borrow::Cow; +use wgpu_types::SurfaceStatus; + +pub fn init_surface(unstable: bool) -> Extension { + Extension::builder("deno_webgpu_surface") + .dependencies(vec!["deno_webidl", "deno_web", "deno_webgpu"]) + .js(include_js_files!( + prefix "deno:deno_webgpu", + "03_surface.js", + "04_surface_idl_types.js", + )) + .ops(vec![ + op_webgpu_surface_configure::decl(), + op_webgpu_surface_get_current_texture::decl(), + op_webgpu_surface_present::decl(), + ]) + .state(move |state| { + // TODO: check & possibly streamline this + // Unstable might be able to be OpMiddleware + // let unstable_checker = state.borrow::(); + // let unstable = unstable_checker.unstable; + state.put(super::Unstable(unstable)); + Ok(()) + }) + .build() +} + +pub struct WebGpuSurface(pub wgpu_core::id::SurfaceId); +impl Resource for WebGpuSurface { + fn name(&self) -> Cow { + "webGPUSurface".into() + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SurfaceConfigureArgs { + surface_rid: ResourceId, + device_rid: ResourceId, + format: wgpu_types::TextureFormat, + usage: u32, + width: u32, + height: u32, + present_mode: Option, + alpha_mode: wgpu_types::CompositeAlphaMode, + view_formats: Vec, +} + +#[op] +pub fn op_webgpu_surface_configure( + state: &mut OpState, + args: SurfaceConfigureArgs, +) -> Result { + let instance = state.borrow::(); + let device_resource = state + .resource_table + .get::(args.device_rid)?; + let device = device_resource.0; + let surface_resource = state + .resource_table + .get::(args.surface_rid)?; + let surface = surface_resource.0; + + let conf = wgpu_types::SurfaceConfiguration::> { + usage: wgpu_types::TextureUsages::from_bits_truncate(args.usage), + format: args.format, + width: args.width, + height: args.height, + present_mode: args.present_mode.unwrap_or_default(), + alpha_mode: args.alpha_mode, + view_formats: args.view_formats, + }; + + let err = + gfx_select!(device => instance.surface_configure(surface, device, &conf)); + + Ok(WebGpuResult::maybe_err(err)) +} + +#[op] +pub fn op_webgpu_surface_get_current_texture( + state: &mut OpState, + device_rid: ResourceId, + surface_rid: ResourceId, +) -> Result { + let instance = state.borrow::(); + let device_resource = state + .resource_table + .get::(device_rid)?; + let device = device_resource.0; + let surface_resource = + state.resource_table.get::(surface_rid)?; + let surface = surface_resource.0; + + let output = + gfx_select!(device => instance.surface_get_current_texture(surface, ()))?; + + match output.status { + SurfaceStatus::Good | SurfaceStatus::Suboptimal => { + let id = output.texture_id.unwrap(); + let rid = state.resource_table.add(crate::texture::WebGpuTexture(id)); + Ok(WebGpuResult::rid(rid)) + } + _ => Err(AnyError::msg("Invalid Surface Status")), + } +} + +#[op] +pub fn op_webgpu_surface_present( + state: &mut OpState, + device_rid: ResourceId, + surface_rid: ResourceId, +) -> Result<(), AnyError> { + let instance = state.borrow::(); + let device_resource = state + .resource_table + .get::(device_rid)?; + let device = device_resource.0; + let surface_resource = + state.resource_table.get::(surface_rid)?; + let surface = surface_resource.0; + + let _ = gfx_select!(device => instance.surface_present(surface))?; + + Ok(()) +} diff --git a/ext/webgpu/src/texture.rs b/ext/webgpu/src/texture.rs index 9a6a1e9397..c4b36c288c 100644 --- a/ext/webgpu/src/texture.rs +++ b/ext/webgpu/src/texture.rs @@ -34,6 +34,7 @@ pub struct CreateTextureArgs { dimension: wgpu_types::TextureDimension, format: wgpu_types::TextureFormat, usage: u32, + view_formats: Vec, } #[op] @@ -55,12 +56,13 @@ pub fn op_webgpu_create_texture( dimension: args.dimension, format: args.format, usage: wgpu_types::TextureUsages::from_bits_truncate(args.usage), + view_formats: args.view_formats, }; gfx_put!(device => instance.device_create_texture( device, &descriptor, - std::marker::PhantomData + () ) => state, WebGpuTexture) } @@ -109,6 +111,6 @@ pub fn op_webgpu_create_texture_view( gfx_put!(texture => instance.texture_create_view( texture, &descriptor, - std::marker::PhantomData + () ) => state, WebGpuTextureView) } diff --git a/ext/webgpu/webgpu.idl b/ext/webgpu/webgpu.idl index cda505715f..4b8865e30d 100644 --- a/ext/webgpu/webgpu.idl +++ b/ext/webgpu/webgpu.idl @@ -13,6 +13,7 @@ interface GPUSupportedLimits { readonly attribute unsigned long maxTextureDimension3D; readonly attribute unsigned long maxTextureArrayLayers; readonly attribute unsigned long maxBindGroups; + readonly attribute unsigned long maxBindingsPerBindGroup; readonly attribute unsigned long maxDynamicUniformBuffersPerPipelineLayout; readonly attribute unsigned long maxDynamicStorageBuffersPerPipelineLayout; readonly attribute unsigned long maxSampledTexturesPerShaderStage; @@ -25,6 +26,7 @@ interface GPUSupportedLimits { readonly attribute unsigned long minUniformBufferOffsetAlignment; readonly attribute unsigned long minStorageBufferOffsetAlignment; readonly attribute unsigned long maxVertexBuffers; + readonly attribute unsigned long long maxBufferSize; readonly attribute unsigned long maxVertexAttributes; readonly attribute unsigned long maxVertexBufferArrayStride; readonly attribute unsigned long maxInterStageShaderComponents; @@ -43,14 +45,10 @@ interface GPUSupportedFeatures { [Exposed=(Window, DedicatedWorker), SecureContext] interface GPUAdapterInfo { - readonly attribute DOMString vendor; - readonly attribute DOMString architecture; - readonly attribute DOMString device; - readonly attribute DOMString description; -}; - -enum GPUPredefinedColorSpace { - "srgb", + readonly attribute DOMString vendor; + readonly attribute DOMString architecture; + readonly attribute DOMString device; + readonly attribute DOMString description; }; interface mixin NavigatorGPU { @@ -71,7 +69,7 @@ dictionary GPURequestAdapterOptions { enum GPUPowerPreference { "low-power", - "high-performance", + "high-performance" }; [Exposed=(Window, DedicatedWorker), SecureContext] @@ -91,7 +89,6 @@ dictionary GPUDeviceDescriptor : GPUObjectDescriptorBase { enum GPUFeatureName { "depth-clip-control", - "depth24unorm-stencil8", "depth32float-stencil8", "texture-compression-bc", "texture-compression-etc2", @@ -133,6 +130,11 @@ GPUDevice includes GPUObjectBase; [Exposed=(Window, DedicatedWorker), SecureContext] interface GPUBuffer { + readonly attribute GPUSize64 size; + readonly attribute GPUBufferUsageFlags usage; + + readonly attribute GPUBufferMapState mapState; + Promise mapAsync(GPUMapModeFlags mode, optional GPUSize64 offset = 0, optional GPUSize64 size); ArrayBuffer getMappedRange(optional GPUSize64 offset = 0, optional GPUSize64 size); undefined unmap(); @@ -141,6 +143,12 @@ interface GPUBuffer { }; GPUBuffer includes GPUObjectBase; +enum GPUBufferMapState { + "unmapped", + "pending", + "mapped" +}; + dictionary GPUBufferDescriptor : GPUObjectDescriptorBase { required GPUSize64 size; required GPUBufferUsageFlags usage; @@ -174,6 +182,15 @@ interface GPUTexture { GPUTextureView createView(optional GPUTextureViewDescriptor descriptor = {}); undefined destroy(); + + readonly attribute GPUIntegerCoordinate width; + readonly attribute GPUIntegerCoordinate height; + readonly attribute GPUIntegerCoordinate depthOrArrayLayers; + readonly attribute GPUIntegerCoordinate mipLevelCount; + readonly attribute GPUSize32 sampleCount; + readonly attribute GPUTextureDimension dimension; + readonly attribute GPUTextureFormat format; + readonly attribute GPUTextureUsageFlags usage; }; GPUTexture includes GPUObjectBase; @@ -184,12 +201,13 @@ dictionary GPUTextureDescriptor : GPUObjectDescriptorBase { GPUTextureDimension dimension = "2d"; required GPUTextureFormat format; required GPUTextureUsageFlags usage; + sequence viewFormats = []; }; enum GPUTextureDimension { "1d", "2d", - "3d", + "3d" }; typedef [EnforceRange] unsigned long GPUTextureUsageFlags; @@ -223,13 +241,13 @@ enum GPUTextureViewDimension { "2d-array", "cube", "cube-array", - "3d", + "3d" }; enum GPUTextureAspect { "all", "stencil-only", - "depth-only", + "depth-only" }; enum GPUTextureFormat { @@ -287,9 +305,6 @@ enum GPUTextureFormat { "depth24plus-stencil8", "depth32float", - // "depth24unorm-stencil8" feature - "depth24unorm-stencil8", - // "depth32float-stencil8" feature "depth32float-stencil8", @@ -352,7 +367,7 @@ enum GPUTextureFormat { "astc-12x10-unorm", "astc-12x10-unorm-srgb", "astc-12x12-unorm", - "astc-12x12-unorm-srgb", + "astc-12x12-unorm-srgb" }; [Exposed=(Window, DedicatedWorker), SecureContext] @@ -376,17 +391,17 @@ dictionary GPUSamplerDescriptor : GPUObjectDescriptorBase { enum GPUAddressMode { "clamp-to-edge", "repeat", - "mirror-repeat", + "mirror-repeat" }; enum GPUFilterMode { "nearest", - "linear", + "linear" }; enum GPUMipmapFilterMode { "nearest", - "linear", + "linear" }; enum GPUCompareFunction { @@ -397,7 +412,7 @@ enum GPUCompareFunction { "greater", "not-equal", "greater-equal", - "always", + "always" }; [Exposed=(Window, DedicatedWorker), SecureContext] @@ -430,7 +445,7 @@ namespace GPUShaderStage { enum GPUBufferBindingType { "uniform", "storage", - "read-only-storage", + "read-only-storage" }; dictionary GPUBufferBindingLayout { @@ -442,7 +457,7 @@ dictionary GPUBufferBindingLayout { enum GPUSamplerBindingType { "filtering", "non-filtering", - "comparison", + "comparison" }; dictionary GPUSamplerBindingLayout { @@ -454,7 +469,7 @@ enum GPUTextureSampleType { "unfilterable-float", "depth", "sint", - "uint", + "uint" }; dictionary GPUTextureBindingLayout { @@ -464,7 +479,7 @@ dictionary GPUTextureBindingLayout { }; enum GPUStorageTextureAccess { - "write-only", + "write-only" }; dictionary GPUStorageTextureBindingLayout { @@ -513,13 +528,12 @@ GPUShaderModule includes GPUObjectBase; dictionary GPUShaderModuleDescriptor : GPUObjectDescriptorBase { required USVString code; - object sourceMap; }; enum GPUCompilationMessageType { "error", "warning", - "info", + "info" }; [Exposed=(Window, DedicatedWorker), Serializable, SecureContext] @@ -555,7 +569,7 @@ dictionary GPUProgrammableStage { record constants; }; -typedef double GPUPipelineConstantValue; // May represent WGSL’s bool, f32, i32, u32. +typedef double GPUPipelineConstantValue; // May represent WGSL’s bool, f32, i32, u32, and f16 if enabled. [Exposed=(Window, DedicatedWorker), SecureContext] interface GPUComputePipeline { @@ -596,18 +610,18 @@ enum GPUPrimitiveTopology { "line-list", "line-strip", "triangle-list", - "triangle-strip", + "triangle-strip" }; enum GPUFrontFace { "ccw", - "cw", + "cw" }; enum GPUCullMode { "none", "front", - "back", + "back" }; dictionary GPUMultisampleState { @@ -661,7 +675,7 @@ enum GPUBlendFactor { "one-minus-dst-alpha", "src-alpha-saturated", "constant", - "one-minus-constant", + "one-minus-constant" }; enum GPUBlendOperation { @@ -669,7 +683,7 @@ enum GPUBlendOperation { "subtract", "reverse-subtract", "min", - "max", + "max" }; dictionary GPUDepthStencilState { @@ -704,12 +718,12 @@ enum GPUStencilOperation { "increment-clamp", "decrement-clamp", "increment-wrap", - "decrement-wrap", + "decrement-wrap" }; enum GPUIndexFormat { "uint16", - "uint32", + "uint32" }; enum GPUVertexFormat { @@ -742,12 +756,12 @@ enum GPUVertexFormat { "sint32", "sint32x2", "sint32x3", - "sint32x4", + "sint32x4" }; enum GPUVertexStepMode { "vertex", - "instance", + "instance" }; dictionary GPUVertexState : GPUProgrammableStage { @@ -767,6 +781,23 @@ dictionary GPUVertexAttribute { required GPUIndex32 shaderLocation; }; +dictionary GPUImageDataLayout { + GPUSize64 offset = 0; + GPUSize32 bytesPerRow; + GPUSize32 rowsPerImage; +}; + +dictionary GPUImageCopyBuffer : GPUImageDataLayout { + required GPUBuffer buffer; +}; + +dictionary GPUImageCopyTexture { + required GPUTexture texture; + GPUIntegerCoordinate mipLevel = 0; + GPUOrigin3D origin = {}; + GPUTextureAspect aspect = "all"; +}; + [Exposed=(Window, DedicatedWorker), SecureContext] interface GPUCommandBuffer { }; @@ -828,31 +859,14 @@ GPUCommandEncoder includes GPUDebugCommandsMixin; dictionary GPUCommandEncoderDescriptor : GPUObjectDescriptorBase { }; -dictionary GPUImageDataLayout { - GPUSize64 offset = 0; - GPUSize32 bytesPerRow; - GPUSize32 rowsPerImage; -}; - -dictionary GPUImageCopyBuffer : GPUImageDataLayout { - required GPUBuffer buffer; -}; - -dictionary GPUImageCopyTexture { - required GPUTexture texture; - GPUIntegerCoordinate mipLevel = 0; - GPUOrigin3D origin = {}; - GPUTextureAspect aspect = "all"; -}; - interface mixin GPUBindingCommandsMixin { undefined setBindGroup(GPUIndex32 index, GPUBindGroup bindGroup, - optional sequence dynamicOffsets = []); + optional sequence dynamicOffsets = []); undefined setBindGroup(GPUIndex32 index, GPUBindGroup bindGroup, - Uint32Array dynamicOffsetsData, - GPUSize64 dynamicOffsetsDataStart, - GPUSize32 dynamicOffsetsDataLength); + Uint32Array dynamicOffsetsData, + GPUSize64 dynamicOffsetsDataStart, + GPUSize32 dynamicOffsetsDataLength); }; interface mixin GPUDebugCommandsMixin { @@ -885,8 +899,8 @@ dictionary GPUComputePassDescriptor : GPUObjectDescriptorBase { [Exposed=(Window, DedicatedWorker), SecureContext] interface GPURenderPassEncoder { undefined setViewport(float x, float y, - float width, float height, - float minDepth, float maxDepth); + float width, float height, + float minDepth, float maxDepth); undefined setScissorRect(GPUIntegerCoordinate x, GPUIntegerCoordinate y, GPUIntegerCoordinate width, GPUIntegerCoordinate height); @@ -914,7 +928,6 @@ GPURenderPassEncoder includes GPURenderCommandsMixin; dictionary GPURenderPassDescriptor : GPUObjectDescriptorBase { required sequence colorAttachments; GPURenderPassDepthStencilAttachment depthStencilAttachment; - GPUQuerySet occlusionQuerySet; }; dictionary GPURenderPassColorAttachment { @@ -942,15 +955,15 @@ dictionary GPURenderPassDepthStencilAttachment { enum GPULoadOp { "load", - "clear", + "clear" }; enum GPUStoreOp { "store", - "discard", + "discard" }; -dictionary GPURenderPassLayout: GPUObjectDescriptorBase { +dictionary GPURenderPassLayout : GPUObjectDescriptorBase { required sequence colorFormats; GPUTextureFormat depthStencilFormat; GPUSize32 sampleCount = 1; @@ -963,11 +976,11 @@ interface mixin GPURenderCommandsMixin { undefined setVertexBuffer(GPUIndex32 slot, GPUBuffer buffer, optional GPUSize64 offset = 0, optional GPUSize64 size); undefined draw(GPUSize32 vertexCount, optional GPUSize32 instanceCount = 1, - optional GPUSize32 firstVertex = 0, optional GPUSize32 firstInstance = 0); + optional GPUSize32 firstVertex = 0, optional GPUSize32 firstInstance = 0); undefined drawIndexed(GPUSize32 indexCount, optional GPUSize32 instanceCount = 1, - optional GPUSize32 firstIndex = 0, - optional GPUSignedOffset32 baseVertex = 0, - optional GPUSize32 firstInstance = 0); + optional GPUSize32 firstIndex = 0, + optional GPUSignedOffset32 baseVertex = 0, + optional GPUSize32 firstInstance = 0); undefined drawIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset); undefined drawIndexedIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset); @@ -1020,6 +1033,9 @@ GPUQueue includes GPUObjectBase; [Exposed=(Window, DedicatedWorker), SecureContext] interface GPUQuerySet { undefined destroy(); + + readonly attribute GPUQueryType type; + readonly attribute GPUSize32 count; }; GPUQuerySet includes GPUObjectBase; @@ -1032,7 +1048,7 @@ dictionary GPUQuerySetDescriptor : GPUObjectDescriptorBase { enum GPUQueryType { "occlusion", "pipeline-statistics", - "timestamp", + "timestamp" }; enum GPUPipelineStatisticName { @@ -1043,8 +1059,31 @@ enum GPUPipelineStatisticName { "compute-shader-invocations", }; +[Exposed=(Window, DedicatedWorker), SecureContext] +interface GPUCanvasContext { + readonly attribute (HTMLCanvasElement or OffscreenCanvas) canvas; + + undefined configure(GPUCanvasConfiguration configuration); + undefined unconfigure(); + + GPUTexture getCurrentTexture(); +}; + +enum GPUCanvasAlphaMode { + "opaque", + "premultiplied" +}; + +dictionary GPUCanvasConfiguration { + required GPUDevice device; + required GPUTextureFormat format; + GPUTextureUsageFlags usage = 0x10; // GPUTextureUsage.RENDER_ATTACHMENT + GPUCanvasAlphaMode alphaMode = "opaque"; + sequence viewFormats = []; +}; + enum GPUDeviceLostReason { - "destroyed", + "destroyed" }; [Exposed=(Window, DedicatedWorker), SecureContext] @@ -1057,44 +1096,31 @@ partial interface GPUDevice { readonly attribute Promise lost; }; -enum GPUErrorFilter { - "out-of-memory", - "validation", -}; - [Exposed=(Window, DedicatedWorker), SecureContext] interface GPUError { readonly attribute DOMString message; }; -[Exposed=(Window, DedicatedWorker), SecureContext] -interface GPUOutOfMemoryError : GPUError { - constructor(DOMString message); -}; - [Exposed=(Window, DedicatedWorker), SecureContext] interface GPUValidationError : GPUError { constructor(DOMString message); }; +[Exposed=(Window, DedicatedWorker), SecureContext] +interface GPUOutOfMemoryError : GPUError { + constructor(DOMString message); +}; + +enum GPUErrorFilter { + "validation", + "out-of-memory" +}; + partial interface GPUDevice { undefined pushErrorScope(GPUErrorFilter filter); Promise popErrorScope(); }; -[Exposed=(Window, DedicatedWorker), SecureContext] -interface GPUUncapturedErrorEvent : Event { - constructor( - DOMString type, - GPUUncapturedErrorEventInit gpuUncapturedErrorEventInitDict - ); - readonly attribute GPUError error; -}; - -dictionary GPUUncapturedErrorEventInit : EventInit { - required GPUError error; -}; - partial interface GPUDevice { [Exposed=(Window, DedicatedWorker)] attribute EventHandler onuncapturederror; diff --git a/runtime/js/98_global_scope.js b/runtime/js/98_global_scope.js index 5a3dfffb02..499538a324 100644 --- a/runtime/js/98_global_scope.js +++ b/runtime/js/98_global_scope.js @@ -144,8 +144,10 @@ GPU: util.nonEnumerable(webgpu.GPU), GPUAdapter: util.nonEnumerable(webgpu.GPUAdapter), + GPUAdapterInfo: util.nonEnumerable(webgpu.GPUAdapterInfo), GPUSupportedLimits: util.nonEnumerable(webgpu.GPUSupportedLimits), GPUSupportedFeatures: util.nonEnumerable(webgpu.GPUSupportedFeatures), + GPUDeviceLostInfo: util.nonEnumerable(webgpu.GPUDeviceLostInfo), GPUDevice: util.nonEnumerable(webgpu.GPUDevice), GPUQueue: util.nonEnumerable(webgpu.GPUQueue), GPUBuffer: util.nonEnumerable(webgpu.GPUBuffer), @@ -170,6 +172,7 @@ GPURenderBundleEncoder: util.nonEnumerable(webgpu.GPURenderBundleEncoder), GPURenderBundle: util.nonEnumerable(webgpu.GPURenderBundle), GPUQuerySet: util.nonEnumerable(webgpu.GPUQuerySet), + GPUError: util.nonEnumerable(webgpu.GPUError), GPUOutOfMemoryError: util.nonEnumerable(webgpu.GPUOutOfMemoryError), GPUValidationError: util.nonEnumerable(webgpu.GPUValidationError), }; diff --git a/tools/wgpu_sync.js b/tools/wgpu_sync.js index ba32670425..6c6130d39f 100755 --- a/tools/wgpu_sync.js +++ b/tools/wgpu_sync.js @@ -3,9 +3,9 @@ import { join, ROOT_PATH } from "./util.js"; -const COMMIT = "076df1a56812eee01614b7a3a4c88798012e79ab"; +const COMMIT = "659f6977051345e4e06ab4832c6f7d268f25a1ad"; const REPO = "gfx-rs/wgpu"; -const V_WGPU = "0.13"; +const V_WGPU = "0.15"; const TARGET_DIR = join(ROOT_PATH, "ext", "webgpu"); async function bash(subcmd, opts = {}) { @@ -59,21 +59,25 @@ async function patchCargo() { data .replace(/^version = .*/m, `version = "${vDenoWebgpu}"`) .replace( - /^wgpu-core \= .*$/gm, - `wgpu-core = { version = "${V_WGPU}", features = ["trace", "replay", "serde"] }`, + /^repository.workspace = true/m, + `repository = "https://github.com/gfx-rs/wgpu"`, ) .replace( - /^wgpu-types \= .*$/gm, - `wgpu-types = { version = "${V_WGPU}", features = ["trace", "replay", "serde"] }`, + /^serde = { workspace = true, features = ["derive"] }/m, + `serde.workspace = true`, + ) + .replace( + /^tokio = { workspace = true, features = ["full"] }/m, + `tokio.workspace = true`, ), - // .replace( - // /^wgpu-core \= .*$/gm, - // `wgpu-core = { git = "https://github.com/${REPO}", rev = "${COMMIT}", features = ["trace", "replay", "serde"] }`, - // ) - // .replace( - // /^wgpu-types \= .*$/gm, - // `wgpu-types = { git = "https://github.com/${REPO}", rev = "${COMMIT}", features = ["trace", "replay", "serde"] }`, - // ) + ); + + await patchFile( + join(ROOT_PATH, "Cargo.toml"), + (data) => + data + .replace(/^wgpu-core = .*/m, `wgpu-core = "${V_WGPU}"`) + .replace(/^wgpu-types = .*/m, `wgpu-types = "${V_WGPU}"`), ); }