diff --git a/Cargo.lock b/Cargo.lock index eb2e05042..69b4d58d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1077,7 +1077,7 @@ dependencies = [ [[package]] name = "deno_webgpu" -version = "0.85.0" +version = "0.110.0" dependencies = [ "deno_core", "raw-window-handle 0.6.0", diff --git a/Cargo.toml b/Cargo.toml index 7cb26434b..52e8c5c17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -170,7 +170,7 @@ deno_core = "0.272.0" deno_url = "0.143.0" deno_web = "0.174.0" deno_webidl = "0.143.0" -deno_webgpu = { version = "0.85.0", path = "./deno_webgpu" } +deno_webgpu = { version = "0.110.0", path = "./deno_webgpu" } tokio = "1.36.0" termcolor = "1.4.1" diff --git a/cts_runner/src/bootstrap.js b/cts_runner/src/bootstrap.js index b16884393..5d9c6f65d 100644 --- a/cts_runner/src/bootstrap.js +++ b/cts_runner/src/bootstrap.js @@ -26,8 +26,9 @@ import { Console } from "ext:deno_console/01_console.js"; import * as url from "ext:deno_url/00_url.js"; import { DOMException } from "ext:deno_web/01_dom_exception.js"; import * as performance from "ext:deno_web/15_performance.js"; -import * as webgpu from "ext:deno_webgpu/01_webgpu.js"; +import { loadWebGPU } from "ext:deno_webgpu/00_init.js"; import * as imageData from "ext:deno_web/16_image_data.js"; +const webgpu = loadWebGPU(); // imports needed to pass module evaluation import "ext:deno_url/01_urlpattern.js"; @@ -39,6 +40,7 @@ import "ext:deno_web/10_filereader.js"; import "ext:deno_web/12_location.js"; import "ext:deno_web/13_message_port.js"; import "ext:deno_web/14_compression.js"; +import "ext:deno_webgpu/02_surface.js"; let globalThis_; diff --git a/cts_runner/src/main.rs b/cts_runner/src/main.rs index 201fda80d..fe8c1cf81 100644 --- a/cts_runner/src/main.rs +++ b/cts_runner/src/main.rs @@ -29,6 +29,9 @@ mod native { .ok_or_else(|| anyhow!("missing specifier in first command line argument"))?; let specifier = resolve_url_or_path(&url, &env::current_dir()?)?; + let mut feature_checker = deno_core::FeatureChecker::default(); + feature_checker.enable_feature(deno_webgpu::UNSTABLE_FEATURE_NAME); + let options = RuntimeOptions { module_loader: Some(Rc::new(deno_core::FsModuleLoader)), get_error_class_fn: Some(&get_error_class_name), @@ -40,9 +43,10 @@ mod native { Arc::new(BlobStore::default()), None, ), - deno_webgpu::deno_webgpu::init_ops_and_esm(true), + deno_webgpu::deno_webgpu::init_ops_and_esm(), cts_runner::init_ops_and_esm(), ], + feature_checker: Some(Arc::new(feature_checker)), ..Default::default() }; let mut js_runtime = JsRuntime::new(options); diff --git a/deno_webgpu/00_init.js b/deno_webgpu/00_init.js new file mode 100644 index 000000000..0f10847ce --- /dev/null +++ b/deno_webgpu/00_init.js @@ -0,0 +1,7 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { core } from "ext:core/mod.js"; + +const loadWebGPU = core.createLazyLoader("ext:deno_webgpu/01_webgpu.js"); + +export { loadWebGPU }; diff --git a/deno_webgpu/01_webgpu.js b/deno_webgpu/01_webgpu.js index 6aecb0323..11d7a5a44 100644 --- a/deno_webgpu/01_webgpu.js +++ b/deno_webgpu/01_webgpu.js @@ -1,4 +1,4 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // @ts-check /// @@ -7,22 +7,100 @@ /// import { core, primordials } from "ext:core/mod.js"; -import * as ops from "ext:core/ops"; -import * as webidl from "ext:deno_webidl/00_webidl.js"; -import { EventTarget } from "ext:deno_web/02_event.js"; -import { DOMException } from "ext:deno_web/01_dom_exception.js"; -import { createFilteredInspectProxy } from "ext:deno_console/01_console.js"; +const { + isDataView, + isTypedArray, +} = core; +import { + op_webgpu_buffer_get_map_async, + op_webgpu_buffer_get_mapped_range, + op_webgpu_buffer_unmap, + op_webgpu_command_encoder_begin_compute_pass, + op_webgpu_command_encoder_begin_render_pass, + op_webgpu_command_encoder_clear_buffer, + op_webgpu_command_encoder_copy_buffer_to_buffer, + op_webgpu_command_encoder_copy_buffer_to_texture, + op_webgpu_command_encoder_copy_texture_to_buffer, + op_webgpu_command_encoder_copy_texture_to_texture, + op_webgpu_command_encoder_finish, + op_webgpu_command_encoder_insert_debug_marker, + op_webgpu_command_encoder_pop_debug_group, + op_webgpu_command_encoder_push_debug_group, + op_webgpu_command_encoder_resolve_query_set, + op_webgpu_command_encoder_write_timestamp, + op_webgpu_compute_pass_dispatch_workgroups, + op_webgpu_compute_pass_dispatch_workgroups_indirect, + op_webgpu_compute_pass_end, + op_webgpu_compute_pass_insert_debug_marker, + op_webgpu_compute_pass_pop_debug_group, + op_webgpu_compute_pass_push_debug_group, + op_webgpu_compute_pass_set_bind_group, + op_webgpu_compute_pass_set_pipeline, + op_webgpu_compute_pipeline_get_bind_group_layout, + op_webgpu_create_bind_group, + op_webgpu_create_bind_group_layout, + op_webgpu_create_buffer, + op_webgpu_create_command_encoder, + op_webgpu_create_compute_pipeline, + op_webgpu_create_pipeline_layout, + op_webgpu_create_query_set, + op_webgpu_create_render_bundle_encoder, + op_webgpu_create_render_pipeline, + op_webgpu_create_sampler, + op_webgpu_create_shader_module, + op_webgpu_create_texture, + op_webgpu_create_texture_view, + op_webgpu_queue_submit, + op_webgpu_render_bundle_encoder_draw, + op_webgpu_render_bundle_encoder_draw_indexed, + op_webgpu_render_bundle_encoder_draw_indirect, + op_webgpu_render_bundle_encoder_finish, + op_webgpu_render_bundle_encoder_insert_debug_marker, + op_webgpu_render_bundle_encoder_pop_debug_group, + op_webgpu_render_bundle_encoder_push_debug_group, + op_webgpu_render_bundle_encoder_set_bind_group, + op_webgpu_render_bundle_encoder_set_index_buffer, + op_webgpu_render_bundle_encoder_set_pipeline, + op_webgpu_render_bundle_encoder_set_vertex_buffer, + op_webgpu_render_pass_begin_occlusion_query, + op_webgpu_render_pass_draw, + op_webgpu_render_pass_draw_indexed, + op_webgpu_render_pass_draw_indexed_indirect, + op_webgpu_render_pass_draw_indirect, + op_webgpu_render_pass_end, + op_webgpu_render_pass_end_occlusion_query, + op_webgpu_render_pass_execute_bundles, + op_webgpu_render_pass_insert_debug_marker, + op_webgpu_render_pass_pop_debug_group, + op_webgpu_render_pass_push_debug_group, + op_webgpu_render_pass_set_bind_group, + op_webgpu_render_pass_set_blend_constant, + op_webgpu_render_pass_set_index_buffer, + op_webgpu_render_pass_set_pipeline, + op_webgpu_render_pass_set_scissor_rect, + op_webgpu_render_pass_set_stencil_reference, + op_webgpu_render_pass_set_vertex_buffer, + op_webgpu_render_pass_set_viewport, + op_webgpu_render_pipeline_get_bind_group_layout, + op_webgpu_request_adapter, + op_webgpu_request_adapter_info, + op_webgpu_request_device, + op_webgpu_write_buffer, + op_webgpu_write_texture, +} from "ext:core/ops"; const { ArrayBuffer, - ArrayBufferIsView, + ArrayBufferPrototypeGetByteLength, ArrayIsArray, ArrayPrototypeFilter, ArrayPrototypeMap, ArrayPrototypePop, ArrayPrototypePush, + DataViewPrototypeGetBuffer, Error, MathMax, ObjectDefineProperty, + ObjectHasOwn, ObjectPrototypeIsPrototypeOf, Promise, PromisePrototypeCatch, @@ -31,17 +109,28 @@ const { PromiseResolve, SafeArrayIterator, SafePromiseAll, - Set, + SafeSet, + SafeWeakRef, SetPrototypeHas, Symbol, SymbolFor, SymbolIterator, TypeError, + TypedArrayPrototypeGetBuffer, + TypedArrayPrototypeGetSymbolToStringTag, Uint32Array, - Uint32ArrayPrototype, Uint8Array, } = primordials; +import * as webidl from "ext:deno_webidl/00_webidl.js"; +import { + Event, + EventTarget, + defineEventHandler, +} from "ext:deno_web/02_event.js"; +import { DOMException } from "ext:deno_web/01_dom_exception.js"; +import { createFilteredInspectProxy } from "ext:deno_console/01_console.js"; + const _rid = Symbol("[[rid]]"); const _size = Symbol("[[size]]"); const _usage = Symbol("[[usage]]"); @@ -228,6 +317,42 @@ class GPUOutOfMemoryError extends GPUError { } const GPUOutOfMemoryErrorPrototype = GPUOutOfMemoryError.prototype; +class GPUInternalError extends GPUError { + name = "GPUInternalError"; + constructor() { + super(illegalConstructorKey); + this[webidl.brand] = webidl.brand; + } +} +const GPUInternalErrorPrototype = GPUInternalError.prototype; + +class GPUUncapturedErrorEvent extends Event { + #error; + + constructor(type, gpuUncapturedErrorEventInitDict) { + super(type, gpuUncapturedErrorEventInitDict); + this[webidl.brand] = webidl.brand; + + const prefix = "Failed to construct 'GPUUncapturedErrorEvent'"; + webidl.requiredArguments(arguments.length, 2, prefix); + gpuUncapturedErrorEventInitDict = webidl.converters + .gpuUncapturedErrorEventInitDict( + gpuUncapturedErrorEventInitDict, + prefix, + "Argument 2", + ); + + this.#error = gpuUncapturedErrorEventInitDict.error; + } + + get error() { + webidl.assertBranded(this, GPUUncapturedErrorEventPrototype); + return this.#error; + } +} +const GPUUncapturedErrorEventPrototype = GPUUncapturedErrorEvent.prototype; +defineEventHandler(GPUUncapturedErrorEvent.prototype, "uncapturederror"); + class GPU { [webidl.brand] = webidl.brand; @@ -238,6 +363,7 @@ class GPU { /** * @param {GPURequestAdapterOptions} options */ + // deno-lint-ignore require-await async requestAdapter(options = {}) { webidl.assertBranded(this, GPUPrototype); options = webidl.converters.GPURequestAdapterOptions( @@ -246,7 +372,7 @@ class GPU { "Argument 1", ); - const { err, ...data } = await ops.op_webgpu_request_adapter( + const { err, ...data } = op_webgpu_request_adapter( options.powerPreference, options.forceFallbackAdapter, ); @@ -258,6 +384,16 @@ class GPU { } } + getPreferredCanvasFormat() { + // Same as Gecko. + // + // https://github.com/mozilla/gecko-dev/blob/b75080bb8b11844d18cb5f9ac6e68a866ef8e243/dom/webgpu/Instance.h#L42-L47 + if (core.build.os == "android") { + return "rgba8unorm"; + } + return "bgra8unorm"; + } + [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) { return `${this.constructor.name} ${inspect({}, inspectOptions)}`; } @@ -303,7 +439,6 @@ class GPUAdapter { } /** @returns {boolean} */ get isFallbackAdapter() { - webidl.assertBranded(this, GPUAdapterPrototype); webidl.assertBranded(this, GPUAdapterPrototype); return this[_adapter].isFallbackAdapter; } @@ -316,6 +451,7 @@ class GPUAdapter { * @param {GPUDeviceDescriptor} descriptor * @returns {Promise} */ + // deno-lint-ignore require-await async requestDevice(descriptor = {}) { webidl.assertBranded(this, GPUAdapterPrototype); const prefix = "Failed to execute 'requestDevice' on 'GPUAdapter'"; @@ -336,7 +472,7 @@ class GPUAdapter { } } - const { rid, features, limits } = await ops.op_webgpu_request_device( + const { rid, queueRid, features, limits } = op_webgpu_request_device( this[_adapter].rid, descriptor.label, requiredFeatures, @@ -349,43 +485,34 @@ class GPUAdapter { features: createGPUSupportedFeatures(features), limits: createGPUSupportedLimits(limits), }); - return createGPUDevice( + const device = createGPUDevice( descriptor.label, inner, - createGPUQueue(descriptor.label, inner), + createGPUQueue(descriptor.label, inner, queueRid), ); + inner.device = device; + return device; } /** - * @param {string[]} unmaskHints * @returns {Promise} */ - async requestAdapterInfo(unmaskHints = []) { + requestAdapterInfo() { webidl.assertBranded(this, GPUAdapterPrototype); - const prefix = "Failed to execute 'requestAdapterInfo' on 'GPUAdapter'"; - unmaskHints = webidl.converters["sequence"]( - unmaskHints, - prefix, - "Argument 1", - ); const { vendor, architecture, device, description, - } = await ops.op_webgpu_request_adapter_info(this[_adapter].rid); + } = op_webgpu_request_adapter_info(this[_adapter].rid); const adapterInfo = webidl.createBranded(GPUAdapterInfo); - adapterInfo[_vendor] = unmaskHints.includes("vendor") ? vendor : ""; - adapterInfo[_architecture] = unmaskHints.includes("architecture") - ? architecture - : ""; - adapterInfo[_device] = unmaskHints.includes("device") ? device : ""; - adapterInfo[_description] = unmaskHints.includes("description") - ? description - : ""; - return adapterInfo; + adapterInfo[_vendor] = vendor; + adapterInfo[_architecture] = architecture; + adapterInfo[_device] = device; + adapterInfo[_description] = description; + return PromiseResolve(adapterInfo); } [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) { @@ -588,6 +715,14 @@ class GPUSupportedLimits { webidl.assertBranded(this, GPUSupportedLimitsPrototype); return this[_limits].maxInterStageShaderComponents; } + get maxColorAttachments() { + webidl.assertBranded(this, GPUSupportedLimitsPrototype); + return this[_limits].maxColorAttachments; + } + get maxColorAttachmentBytesPerSample() { + webidl.assertBranded(this, GPUSupportedLimitsPrototype); + return this[_limits].maxColorAttachmentBytesPerSample; + } get maxComputeWorkgroupStorageSize() { webidl.assertBranded(this, GPUSupportedLimitsPrototype); return this[_limits].maxComputeWorkgroupStorageSize; @@ -617,7 +752,10 @@ class GPUSupportedLimits { return inspect( createFilteredInspectProxy({ object: this, - evaluate: ObjectPrototypeIsPrototypeOf(GPUSupportedLimitsPrototype, this), + evaluate: ObjectPrototypeIsPrototypeOf( + GPUSupportedLimitsPrototype, + this, + ), keys: [ "maxTextureDimension1D", "maxTextureDimension2D", @@ -641,6 +779,8 @@ class GPUSupportedLimits { "maxVertexAttributes", "maxVertexBufferArrayStride", "maxInterStageShaderComponents", + "maxColorAttachments", + "maxColorAttachmentBytesPerSample", "maxComputeWorkgroupStorageSize", "maxComputeInvocationsPerWorkgroup", "maxComputeWorkgroupSizeX", @@ -658,7 +798,7 @@ const GPUSupportedLimitsPrototype = GPUSupportedLimits.prototype; function createGPUSupportedFeatures(features) { /** @type {GPUSupportedFeatures} */ const supportedFeatures = webidl.createBranded(GPUSupportedFeatures); - supportedFeatures[webidl.setlikeInner] = new Set(features); + supportedFeatures[webidl.setlikeInner] = new SafeSet(features); webidl.setlike( supportedFeatures, GPUSupportedFeaturesPrototype, @@ -676,8 +816,7 @@ class GPUSupportedFeatures { if (ObjectPrototypeIsPrototypeOf(GPUSupportedFeaturesPrototype, this)) { return `${this.constructor.name} ${ // deno-lint-ignore prefer-primordials - inspect([...this], inspectOptions) - }`; + inspect([...this], inspectOptions)}`; } else { return `${this.constructor.name} ${inspect({}, inspectOptions)}`; } @@ -721,7 +860,10 @@ class GPUDeviceLostInfo { return inspect( createFilteredInspectProxy({ object: this, - evaluate: ObjectPrototypeIsPrototypeOf(GPUDeviceLostInfoPrototype, this), + evaluate: ObjectPrototypeIsPrototypeOf( + GPUDeviceLostInfoPrototype, + this, + ), keys: [ "reason", "message", @@ -775,6 +917,7 @@ function GPUObjectBaseMixin(name, type) { * @property {number | undefined} rid * @property {GPUSupportedFeatures} features * @property {GPUSupportedLimits} limits + * @property {GPUDevice} device */ class InnerGPUDevice { @@ -786,7 +929,7 @@ class InnerGPUDevice { features; /** @type {GPUSupportedLimits} */ limits; - /** @type {WeakRef[]} */ + /** @type {SafeWeakRef[]} */ resources; /** @type {boolean} */ isLost; @@ -796,6 +939,8 @@ class InnerGPUDevice { resolveLost; /** @type {ErrorScope[]} */ errorScopeStack; + /** @type {GPUDevice} */ + device; /** * @param {InnerGPUDeviceOptions} options @@ -816,7 +961,7 @@ class InnerGPUDevice { /** @param {any} resource */ trackResource(resource) { - ArrayPrototypePush(this.resources, new WeakRef(resource)); + ArrayPrototypePush(this.resources, new SafeWeakRef(resource)); } /** @param {{ type: string, value: string | null } | undefined} err */ @@ -841,6 +986,8 @@ class InnerGPUDevice { ); case "out-of-memory": return PromiseReject(new GPUOutOfMemoryError()); + case "internal": + return PromiseReject(new GPUInternalError()); } } }); @@ -865,8 +1012,12 @@ class InnerGPUDevice { validationFilteredPromise, ); } else { - PromisePrototypeCatch(validationFilteredPromise, () => { - // TODO(lucacasonato): emit an UncapturedErrorEvent + PromisePrototypeCatch(validationFilteredPromise, (err) => { + this.device.dispatchEvent( + new GPUUncapturedErrorEvent("uncapturederror", { + error: err, + }), + ); }); } // prevent uncaptured promise rejections @@ -886,12 +1037,41 @@ class InnerGPUDevice { if (oomScope) { ArrayPrototypePush(oomScope.operations, oomFilteredPromise); } else { - PromisePrototypeCatch(oomFilteredPromise, () => { - // TODO(lucacasonato): emit an UncapturedErrorEvent + PromisePrototypeCatch(oomFilteredPromise, (err) => { + this.device.dispatchEvent( + new GPUUncapturedErrorEvent("uncapturederror", { + error: err, + }), + ); }); } // prevent uncaptured promise rejections PromisePrototypeCatch(oomFilteredPromise, (_err) => {}); + + const internalStack = ArrayPrototypeFilter( + this.errorScopeStack, + ({ filter }) => filter == "internal", + ); + const internalScope = internalStack[internalStack.length - 1]; + const internalFilteredPromise = PromisePrototypeCatch(operation, (err) => { + if (ObjectPrototypeIsPrototypeOf(GPUInternalErrorPrototype, err)) { + return PromiseReject(err); + } + return PromiseResolve(); + }); + if (internalScope) { + ArrayPrototypePush(internalScope.operations, internalFilteredPromise); + } else { + PromisePrototypeCatch(internalFilteredPromise, (err) => { + this.device.dispatchEvent( + new GPUUncapturedErrorEvent("uncapturederror", { + error: err, + }), + ); + }); + } + // prevent uncaptured promise rejections + PromisePrototypeCatch(internalFilteredPromise, (_err) => {}); } } @@ -971,7 +1151,7 @@ class GPUDevice extends EventTarget { "Argument 1", ); const device = assertDevice(this, prefix, "this"); - const { rid, err } = ops.op_webgpu_create_buffer( + const { rid, err } = op_webgpu_create_buffer( device.rid, descriptor.label, descriptor.size, @@ -1022,7 +1202,7 @@ class GPUDevice extends EventTarget { "Argument 1", ); const device = assertDevice(this, prefix, "this"); - const { rid, err } = ops.op_webgpu_create_texture({ + const { rid, err } = op_webgpu_create_texture({ deviceRid: device.rid, ...descriptor, size: normalizeGPUExtent3D(descriptor.size), @@ -1051,7 +1231,7 @@ class GPUDevice extends EventTarget { "Argument 1", ); const device = assertDevice(this, prefix, "this"); - const { rid, err } = ops.op_webgpu_create_sampler({ + const { rid, err } = op_webgpu_create_sampler({ deviceRid: device.rid, ...descriptor, }); @@ -1084,6 +1264,7 @@ class GPUDevice extends EventTarget { const entry = descriptor.entries[i]; let j = 0; + // deno-lint-ignore prefer-primordials if (entry.buffer) j++; if (entry.sampler) j++; if (entry.texture) j++; @@ -1094,7 +1275,7 @@ class GPUDevice extends EventTarget { } } - const { rid, err } = ops.op_webgpu_create_bind_group_layout( + const { rid, err } = op_webgpu_create_bind_group_layout( device.rid, descriptor.label, descriptor.entries, @@ -1137,7 +1318,7 @@ class GPUDevice extends EventTarget { return rid; }, ); - const { rid, err } = ops.op_webgpu_create_pipeline_layout( + const { rid, err } = op_webgpu_create_pipeline_layout( device.rid, descriptor.label, bindGroupLayouts, @@ -1204,7 +1385,9 @@ class GPUDevice extends EventTarget { resource: rid, }; } else { + // deno-lint-ignore prefer-primordials const rid = assertResource(resource.buffer, prefix, context); + // deno-lint-ignore prefer-primordials assertDeviceMatch(device, resource.buffer, { prefix, resourceContext: context, @@ -1220,7 +1403,7 @@ class GPUDevice extends EventTarget { } }); - const { rid, err } = ops.op_webgpu_create_bind_group( + const { rid, err } = op_webgpu_create_bind_group( device.rid, descriptor.label, layout, @@ -1250,7 +1433,7 @@ class GPUDevice extends EventTarget { "Argument 1", ); const device = assertDevice(this, prefix, "this"); - const { rid, err } = ops.op_webgpu_create_shader_module( + const { rid, err } = op_webgpu_create_shader_module( device.rid, descriptor.label, descriptor.code, @@ -1301,7 +1484,7 @@ class GPUDevice extends EventTarget { selfContext: "this", }); - const { rid, err } = ops.op_webgpu_create_compute_pipeline( + const { rid, err } = op_webgpu_create_compute_pipeline( device.rid, descriptor.label, layout, @@ -1375,7 +1558,7 @@ class GPUDevice extends EventTarget { }; } - const { rid, err } = ops.op_webgpu_create_render_pipeline({ + const { rid, err } = op_webgpu_create_render_pipeline({ deviceRid: device.rid, label: descriptor.label, layout, @@ -1402,12 +1585,166 @@ class GPUDevice extends EventTarget { createComputePipelineAsync(descriptor) { // TODO(lucacasonato): this should be real async - return PromiseResolve(this.createComputePipeline(descriptor)); + + webidl.assertBranded(this, GPUDevicePrototype); + const prefix = + "Failed to execute 'createComputePipelineAsync' on 'GPUDevice'"; + webidl.requiredArguments(arguments.length, 1, prefix); + descriptor = webidl.converters.GPUComputePipelineDescriptor( + descriptor, + prefix, + "Argument 1", + ); + const device = assertDevice(this, prefix, "this"); + let layout = descriptor.layout; + if (typeof descriptor.layout !== "string") { + const context = "layout"; + layout = assertResource(descriptor.layout, prefix, context); + assertDeviceMatch(device, descriptor.layout, { + prefix, + resourceContext: context, + selfContext: "this", + }); + } + const module = assertResource( + descriptor.compute.module, + prefix, + "compute shader module", + ); + assertDeviceMatch(device, descriptor.compute.module, { + prefix, + resourceContext: "compute shader module", + selfContext: "this", + }); + + const { rid, err } = op_webgpu_create_compute_pipeline( + device.rid, + descriptor.label, + layout, + { + module, + entryPoint: descriptor.compute.entryPoint, + constants: descriptor.compute.constants, + }, + ); + device.pushError(err); + if (err) { + switch (err.type) { + case "validation": + return PromiseReject( + new GPUPipelineError(err.value ?? "validation error", { + reason: "validation", + }), + ); + case "internal": + return PromiseReject( + new GPUPipelineError("internal error", { + reason: "validation", + }), + ); + } + } + + const computePipeline = createGPUComputePipeline( + descriptor.label, + device, + rid, + ); + device.trackResource(computePipeline); + return PromiseResolve(computePipeline); } createRenderPipelineAsync(descriptor) { // TODO(lucacasonato): this should be real async - return PromiseResolve(this.createRenderPipeline(descriptor)); + + webidl.assertBranded(this, GPUDevicePrototype); + const prefix = + "Failed to execute 'createRenderPipelineAsync' on 'GPUDevice'"; + webidl.requiredArguments(arguments.length, 1, prefix); + descriptor = webidl.converters.GPURenderPipelineDescriptor( + descriptor, + prefix, + "Argument 1", + ); + const device = assertDevice(this, prefix, "this"); + let layout = descriptor.layout; + if (typeof descriptor.layout !== "string") { + const context = "layout"; + layout = assertResource(descriptor.layout, prefix, context); + assertDeviceMatch(device, descriptor.layout, { + prefix, + resourceContext: context, + selfContext: "this", + }); + } + const module = assertResource( + descriptor.vertex.module, + prefix, + "vertex shader module", + ); + assertDeviceMatch(device, descriptor.vertex.module, { + prefix, + resourceContext: "vertex shader module", + selfContext: "this", + }); + let fragment = undefined; + if (descriptor.fragment) { + const module = assertResource( + descriptor.fragment.module, + prefix, + "fragment shader module", + ); + assertDeviceMatch(device, descriptor.fragment.module, { + prefix, + resourceContext: "fragment shader module", + selfContext: "this", + }); + fragment = { + module, + entryPoint: descriptor.fragment.entryPoint, + targets: descriptor.fragment.targets, + }; + } + + const { rid, err } = op_webgpu_create_render_pipeline({ + deviceRid: device.rid, + label: descriptor.label, + layout, + vertex: { + module, + entryPoint: descriptor.vertex.entryPoint, + buffers: descriptor.vertex.buffers, + }, + primitive: descriptor.primitive, + depthStencil: descriptor.depthStencil, + multisample: descriptor.multisample, + fragment, + }); + device.pushError(err); + if (err) { + switch (err.type) { + case "validation": + return PromiseReject( + new GPUPipelineError(err.value ?? "validation error", { + reason: "validation", + }), + ); + case "internal": + return PromiseReject( + new GPUPipelineError("internal error", { + reason: "validation", + }), + ); + } + } + + const renderPipeline = createGPURenderPipeline( + descriptor.label, + device, + rid, + ); + device.trackResource(renderPipeline); + return renderPipeline; } /** @@ -1423,7 +1760,7 @@ class GPUDevice extends EventTarget { "Argument 1", ); const device = assertDevice(this, prefix, "this"); - const { rid, err } = ops.op_webgpu_create_command_encoder( + const { rid, err } = op_webgpu_create_command_encoder( device.rid, descriptor.label, ); @@ -1453,7 +1790,7 @@ class GPUDevice extends EventTarget { "Argument 1", ); const device = assertDevice(this, prefix, "this"); - const { rid, err } = ops.op_webgpu_create_render_bundle_encoder({ + const { rid, err } = op_webgpu_create_render_bundle_encoder({ deviceRid: device.rid, ...descriptor, }); @@ -1482,7 +1819,7 @@ class GPUDevice extends EventTarget { "Argument 1", ); const device = assertDevice(this, prefix, "this"); - const { rid, err } = ops.op_webgpu_create_query_set({ + const { rid, err } = op_webgpu_create_query_set({ deviceRid: device.rid, ...descriptor, }); @@ -1570,22 +1907,49 @@ class GPUDevice extends EventTarget { GPUObjectBaseMixin("GPUDevice", GPUDevice); const GPUDevicePrototype = GPUDevice.prototype; +class GPUPipelineError extends DOMException { + #reason; + + constructor(message = "", options = {}) { + const prefix = "Failed to construct 'GPUPipelineError'"; + message = webidl.converters.DOMString(message, prefix, "Argument 1"); + options = webidl.converters.GPUPipelineErrorInit( + options, + prefix, + "Argument 2", + ); + super(message, "GPUPipelineError"); + + this.#reason = options.reason; + } + + get reason() { + webidl.assertBranded(this, GPUPipelineErrorPrototype); + return this.#reason; + } +} +const GPUPipelineErrorPrototype = GPUPipelineError.prototype; + /** * @param {string | null} label * @param {InnerGPUDevice} device + * @param {number} rid * @returns {GPUQueue} */ -function createGPUQueue(label, device) { +function createGPUQueue(label, device, rid) { /** @type {GPUQueue} */ const queue = webidl.createBranded(GPUQueue); queue[_label] = label; queue[_device] = device; + queue[_rid] = rid; return queue; } class GPUQueue { /** @type {InnerGPUDevice} */ [_device]; + /** @type {number} */ + [_rid]; constructor() { webidl.illegalConstructor(); @@ -1619,7 +1983,7 @@ class GPUQueue { return rid; }, ); - const { err } = ops.op_webgpu_queue_submit(device.rid, commandBufferRids); + const { err } = op_webgpu_queue_submit(this[_rid], commandBufferRids); for (let i = 0; i < commandBuffers.length; ++i) { commandBuffers[i][_rid] = undefined; } @@ -1664,13 +2028,23 @@ class GPUQueue { selfContext: "this", resourceContext: "Argument 1", }); - const { err } = ops.op_webgpu_write_buffer( - device.rid, + /** @type {ArrayBufferLike} */ + let abLike = data; + if (isTypedArray(data)) { + abLike = TypedArrayPrototypeGetBuffer( + /** @type {Uint8Array} */ (data), + ); + } else if (isDataView(data)) { + abLike = DataViewPrototypeGetBuffer(/** @type {DataView} */ (data)); + } + + const { err } = op_webgpu_write_buffer( + this[_rid], bufferRid, bufferOffset, dataOffset, size, - new Uint8Array(ArrayBufferIsView(data) ? data.buffer : data), + new Uint8Array(abLike), ); device.pushError(err); } @@ -1704,8 +2078,19 @@ class GPUQueue { selfContext: "this", resourceContext: "texture", }); - const { err } = ops.op_webgpu_write_texture( - device.rid, + + /** @type {ArrayBufferLike} */ + let abLike = data; + if (isTypedArray(data)) { + abLike = TypedArrayPrototypeGetBuffer( + /** @type {Uint8Array} */ (data), + ); + } else if (isDataView(data)) { + abLike = DataViewPrototypeGetBuffer(/** @type {DataView} */ (data)); + } + + const { err } = op_webgpu_write_texture( + this[_rid], { texture: textureRid, mipLevel: destination.mipLevel, @@ -1716,7 +2101,7 @@ class GPUQueue { }, dataLayout, normalizeGPUExtent3D(size), - new Uint8Array(ArrayBufferIsView(data) ? data.buffer : data), + new Uint8Array(abLike), ); device.pushError(err); } @@ -1900,8 +2285,7 @@ class GPUBuffer { this[_mapMode] = mode; this[_state] = "pending"; const promise = PromisePrototypeThen( - core.opAsync( - "op_webgpu_buffer_get_map_async", + op_webgpu_buffer_get_map_async( bufferRid, device.rid, mode, @@ -1947,9 +2331,9 @@ class GPUBuffer { throw new DOMException(`${prefix}: invalid state.`, "OperationError"); } for (let i = 0; i < mappedRanges.length; ++i) { - const [buffer, _rid, start] = mappedRanges[i]; + const { 0: buffer, 1: _rid, 2: start } = mappedRanges[i]; // TODO(lucacasonato): is this logic correct? - const end = start + buffer.byteLength; + const end = start + ArrayBufferPrototypeGetByteLength(buffer); if ( (start >= offset && start < (offset + rangeSize)) || (end >= offset && end < (offset + rangeSize)) @@ -1962,7 +2346,7 @@ class GPUBuffer { } const buffer = new ArrayBuffer(rangeSize); - const { rid } = ops.op_webgpu_buffer_get_mapped_range( + const { rid } = op_webgpu_buffer_get_mapped_range( bufferRid, offset, size, @@ -2016,8 +2400,8 @@ class GPUBuffer { throw new DOMException(`${prefix}: invalid state.`, "OperationError"); } for (let i = 0; i < mappedRanges.length; ++i) { - const [buffer, mappedRid] = mappedRanges[i]; - const { err } = ops.op_webgpu_buffer_unmap( + const { 0: buffer, 1: mappedRid } = mappedRanges[i]; + const { err } = op_webgpu_buffer_unmap( bufferRid, mappedRid, ...new SafeArrayIterator(write ? [new Uint8Array(buffer)] : []), @@ -2135,7 +2519,7 @@ class GPUTexture { [_device]; /** @type {number | undefined} */ [_rid]; - /** @type {WeakRef[]} */ + /** @type {SafeWeakRef[]} */ [_views]; /** @type {number} */ @@ -2189,7 +2573,7 @@ class GPUTexture { ); const device = assertDevice(this, prefix, "this"); const textureRid = assertResource(this, prefix, "this"); - const { rid, err } = ops.op_webgpu_create_texture_view({ + const { rid, err } = op_webgpu_create_texture_view({ textureRid, ...descriptor, }); @@ -2200,7 +2584,7 @@ class GPUTexture { this, rid, ); - ArrayPrototypePush(this[_views], new WeakRef(textureView)); + ArrayPrototypePush(this[_views], new SafeWeakRef(textureView)); return textureView; } @@ -2423,7 +2807,10 @@ class GPUBindGroupLayout { return inspect( createFilteredInspectProxy({ object: this, - evaluate: ObjectPrototypeIsPrototypeOf(GPUBindGroupLayoutPrototype, this), + evaluate: ObjectPrototypeIsPrototypeOf( + GPUBindGroupLayoutPrototype, + this, + ), keys: [ "label", ], @@ -2471,7 +2858,10 @@ class GPUPipelineLayout { return inspect( createFilteredInspectProxy({ object: this, - evaluate: ObjectPrototypeIsPrototypeOf(GPUPipelineLayoutPrototype, this), + evaluate: ObjectPrototypeIsPrototypeOf( + GPUPipelineLayoutPrototype, + this, + ), keys: [ "label", ], @@ -2483,7 +2873,6 @@ class GPUPipelineLayout { GPUObjectBaseMixin("GPUPipelineLayout", GPUPipelineLayout); const GPUPipelineLayoutPrototype = GPUPipelineLayout.prototype; - /** * @param {string | null} label * @param {InnerGPUDevice} device @@ -2643,8 +3032,8 @@ class GPUComputePipeline { index = webidl.converters["unsigned long"](index, prefix, "Argument 1"); const device = assertDevice(this, prefix, "this"); const computePipelineRid = assertResource(this, prefix, "this"); - const { rid, label, err } = ops - .op_webgpu_compute_pipeline_get_bind_group_layout( + const { rid, label, err } = + op_webgpu_compute_pipeline_get_bind_group_layout( computePipelineRid, index, ); @@ -2663,7 +3052,10 @@ class GPUComputePipeline { return inspect( createFilteredInspectProxy({ object: this, - evaluate: ObjectPrototypeIsPrototypeOf(GPUComputePipelinePrototype, this), + evaluate: ObjectPrototypeIsPrototypeOf( + GPUComputePipelinePrototype, + this, + ), keys: [ "label", ], @@ -2719,11 +3111,10 @@ class GPURenderPipeline { index = webidl.converters["unsigned long"](index, prefix, "Argument 1"); const device = assertDevice(this, prefix, "this"); const renderPipelineRid = assertResource(this, prefix, "this"); - const { rid, label, err } = ops - .op_webgpu_render_pipeline_get_bind_group_layout( - renderPipelineRid, - index, - ); + const { rid, label, err } = op_webgpu_render_pipeline_get_bind_group_layout( + renderPipelineRid, + index, + ); device.pushError(err); const bindGroupLayout = createGPUBindGroupLayout( @@ -2739,7 +3130,10 @@ class GPURenderPipeline { return inspect( createFilteredInspectProxy({ object: this, - evaluate: ObjectPrototypeIsPrototypeOf(GPURenderPipelinePrototype, this), + evaluate: ObjectPrototypeIsPrototypeOf( + GPURenderPipelinePrototype, + this, + ), keys: [ "label", ], @@ -2793,7 +3187,7 @@ class GPUCommandEncoder { [_device]; /** @type {number | undefined} */ [_rid]; - /** @type {WeakRef[]} */ + /** @type {SafeWeakRef[]} */ [_encoders]; [_cleanup]() { @@ -2843,7 +3237,7 @@ class GPUCommandEncoder { if (descriptor.depthStencilAttachment) { if ( descriptor.depthStencilAttachment.depthLoadOp === "clear" && - !("depthClearValue" in descriptor.depthStencilAttachment) + !(ObjectHasOwn(descriptor.depthStencilAttachment, "depthClearValue")) ) { throw webidl.makeException( TypeError, @@ -2903,7 +3297,11 @@ class GPUCommandEncoder { prefix, `resolve target texture view for ${context}`, ); - assertResource(colorAttachment.resolveTarget[_texture], prefix, `texture backing resolve target texture view for ${context}`); + assertResource( + colorAttachment.resolveTarget[_texture], + prefix, + `texture backing resolve target texture view for ${context}`, + ); assertDeviceMatch( device, colorAttachment.resolveTarget[_texture], @@ -2936,16 +3334,21 @@ class GPUCommandEncoder { let timestampWrites = null; if (descriptor.timestampWrites) { - const querySet = assertResource(descriptor.timestampWrites.querySet, prefix, "querySet"); + const querySet = assertResource( + descriptor.timestampWrites.querySet, + prefix, + "querySet", + ); timestampWrites = { querySet, - beginningOfPassWriteIndex: descriptor.timestampWrites.beginningOfPassWriteIndex, + beginningOfPassWriteIndex: + descriptor.timestampWrites.beginningOfPassWriteIndex, endOfPassWriteIndex: descriptor.timestampWrites.endOfPassWriteIndex, }; } - const { rid } = ops.op_webgpu_command_encoder_begin_render_pass( + const { rid } = op_webgpu_command_encoder_begin_render_pass( commandEncoderRid, descriptor.label, colorAttachments, @@ -2959,7 +3362,7 @@ class GPUCommandEncoder { this, rid, ); - ArrayPrototypePush(this[_encoders], new WeakRef(renderPassEncoder)); + ArrayPrototypePush(this[_encoders], new SafeWeakRef(renderPassEncoder)); return renderPassEncoder; } @@ -2981,16 +3384,21 @@ class GPUCommandEncoder { let timestampWrites = null; if (descriptor.timestampWrites) { - const querySet = assertResource(descriptor.timestampWrites.querySet, prefix, "querySet"); + const querySet = assertResource( + descriptor.timestampWrites.querySet, + prefix, + "querySet", + ); timestampWrites = { querySet, - beginningOfPassWriteIndex: descriptor.timestampWrites.beginningOfPassWriteIndex, + beginningOfPassWriteIndex: + descriptor.timestampWrites.beginningOfPassWriteIndex, endOfPassWriteIndex: descriptor.timestampWrites.endOfPassWriteIndex, }; } - const { rid } = ops.op_webgpu_command_encoder_begin_compute_pass( + const { rid } = op_webgpu_command_encoder_begin_compute_pass( commandEncoderRid, descriptor.label, timestampWrites, @@ -3001,7 +3409,7 @@ class GPUCommandEncoder { this, rid, ); - ArrayPrototypePush(this[_encoders], new WeakRef(computePassEncoder)); + ArrayPrototypePush(this[_encoders], new SafeWeakRef(computePassEncoder)); return computePassEncoder; } @@ -3055,7 +3463,7 @@ class GPUCommandEncoder { selfContext: "this", }); - const { err } = ops.op_webgpu_command_encoder_copy_buffer_to_buffer( + const { err } = op_webgpu_command_encoder_copy_buffer_to_buffer( commandEncoderRid, sourceRid, sourceOffset, @@ -3086,10 +3494,12 @@ class GPUCommandEncoder { const device = assertDevice(this, prefix, "this"); const commandEncoderRid = assertResource(this, prefix, "this"); const sourceBufferRid = assertResource( + // deno-lint-ignore prefer-primordials source.buffer, prefix, "source in Argument 1", ); + // deno-lint-ignore prefer-primordials assertDeviceMatch(device, source.buffer, { prefix, resourceContext: "source in Argument 1", @@ -3106,7 +3516,7 @@ class GPUCommandEncoder { selfContext: "this", }); - const { err } = ops.op_webgpu_command_encoder_copy_buffer_to_texture( + const { err } = op_webgpu_command_encoder_copy_buffer_to_texture( commandEncoderRid, { ...source, @@ -3159,16 +3569,18 @@ class GPUCommandEncoder { selfContext: "this", }); const destinationBufferRid = assertResource( + // deno-lint-ignore prefer-primordials destination.buffer, prefix, "buffer in Argument 2", ); + // deno-lint-ignore prefer-primordials assertDeviceMatch(device, destination.buffer, { prefix, resourceContext: "buffer in Argument 2", selfContext: "this", }); - const { err } = ops.op_webgpu_command_encoder_copy_texture_to_buffer( + const { err } = op_webgpu_command_encoder_copy_texture_to_buffer( commandEncoderRid, { texture: sourceTextureRid, @@ -3228,7 +3640,7 @@ class GPUCommandEncoder { resourceContext: "texture in Argument 2", selfContext: "this", }); - const { err } = ops.op_webgpu_command_encoder_copy_texture_to_texture( + const { err } = op_webgpu_command_encoder_copy_texture_to_texture( commandEncoderRid, { texture: sourceTextureRid, @@ -3264,7 +3676,7 @@ class GPUCommandEncoder { const device = assertDevice(this, prefix, "this"); const commandEncoderRid = assertResource(this, prefix, "this"); const bufferRid = assertResource(buffer, prefix, "Argument 1"); - const { err } = ops.op_webgpu_command_encoder_clear_buffer( + const { err } = op_webgpu_command_encoder_clear_buffer( commandEncoderRid, bufferRid, offset, @@ -3283,7 +3695,7 @@ class GPUCommandEncoder { groupLabel = webidl.converters.USVString(groupLabel, prefix, "Argument 1"); const device = assertDevice(this, prefix, "this"); const commandEncoderRid = assertResource(this, prefix, "this"); - const { err } = ops.op_webgpu_command_encoder_push_debug_group( + const { err } = op_webgpu_command_encoder_push_debug_group( commandEncoderRid, groupLabel, ); @@ -3295,7 +3707,7 @@ class GPUCommandEncoder { const prefix = "Failed to execute 'popDebugGroup' on 'GPUCommandEncoder'"; const device = assertDevice(this, prefix, "this"); const commandEncoderRid = assertResource(this, prefix, "this"); - const { err } = ops.op_webgpu_command_encoder_pop_debug_group( + const { err } = op_webgpu_command_encoder_pop_debug_group( commandEncoderRid, ); device.pushError(err); @@ -3316,7 +3728,7 @@ class GPUCommandEncoder { ); const device = assertDevice(this, prefix, "this"); const commandEncoderRid = assertResource(this, prefix, "this"); - const { err } = ops.op_webgpu_command_encoder_insert_debug_marker( + const { err } = op_webgpu_command_encoder_insert_debug_marker( commandEncoderRid, markerLabel, ); @@ -3341,7 +3753,7 @@ class GPUCommandEncoder { resourceContext: "Argument 1", selfContext: "this", }); - const { err } = ops.op_webgpu_command_encoder_write_timestamp( + const { err } = op_webgpu_command_encoder_write_timestamp( commandEncoderRid, querySetRid, queryIndex, @@ -3393,7 +3805,7 @@ class GPUCommandEncoder { resourceContext: "Argument 3", selfContext: "this", }); - const { err } = ops.op_webgpu_command_encoder_resolve_query_set( + const { err } = op_webgpu_command_encoder_resolve_query_set( commandEncoderRid, querySetRid, firstQuery, @@ -3418,7 +3830,7 @@ class GPUCommandEncoder { ); const device = assertDevice(this, prefix, "this"); const commandEncoderRid = assertResource(this, prefix, "this"); - const { rid, err } = ops.op_webgpu_command_encoder_finish( + const { rid, err } = op_webgpu_command_encoder_finish( commandEncoderRid, descriptor.label, ); @@ -3439,7 +3851,10 @@ class GPUCommandEncoder { return inspect( createFilteredInspectProxy({ object: this, - evaluate: ObjectPrototypeIsPrototypeOf(GPUCommandEncoderPrototype, this), + evaluate: ObjectPrototypeIsPrototypeOf( + GPUCommandEncoderPrototype, + this, + ), keys: [ "label", ], @@ -3506,7 +3921,7 @@ class GPURenderPassEncoder { assertDevice(this[_encoder], prefix, "encoder referenced by this"); assertResource(this[_encoder], prefix, "encoder referenced by this"); const renderPassRid = assertResource(this, prefix, "this"); - ops.op_webgpu_render_pass_set_viewport({ + op_webgpu_render_pass_set_viewport({ renderPassRid, x, y, @@ -3539,7 +3954,7 @@ class GPURenderPassEncoder { assertDevice(this[_encoder], prefix, "encoder referenced by this"); assertResource(this[_encoder], prefix, "encoder referenced by this"); const renderPassRid = assertResource(this, prefix, "this"); - ops.op_webgpu_render_pass_set_scissor_rect( + op_webgpu_render_pass_set_scissor_rect( renderPassRid, x, y, @@ -3560,7 +3975,7 @@ class GPURenderPassEncoder { assertDevice(this[_encoder], prefix, "encoder referenced by this"); assertResource(this[_encoder], prefix, "encoder referenced by this"); const renderPassRid = assertResource(this, prefix, "this"); - ops.op_webgpu_render_pass_set_blend_constant( + op_webgpu_render_pass_set_blend_constant( renderPassRid, normalizeGPUColor(color), ); @@ -3582,7 +3997,7 @@ class GPURenderPassEncoder { assertDevice(this[_encoder], prefix, "encoder referenced by this"); assertResource(this[_encoder], prefix, "encoder referenced by this"); const renderPassRid = assertResource(this, prefix, "this"); - ops.op_webgpu_render_pass_set_stencil_reference( + op_webgpu_render_pass_set_stencil_reference( renderPassRid, reference, ); @@ -3600,7 +4015,7 @@ class GPURenderPassEncoder { assertDevice(this[_encoder], prefix, "encoder referenced by this"); assertResource(this[_encoder], prefix, "encoder referenced by this"); const renderPassRid = assertResource(this, prefix, "this"); - ops.op_webgpu_render_pass_begin_occlusion_query( + op_webgpu_render_pass_begin_occlusion_query( renderPassRid, queryIndex, ); @@ -3613,7 +4028,7 @@ class GPURenderPassEncoder { assertDevice(this[_encoder], prefix, "encoder referenced by this"); assertResource(this[_encoder], prefix, "encoder referenced by this"); const renderPassRid = assertResource(this, prefix, "this"); - ops.op_webgpu_render_pass_end_occlusion_query(renderPassRid); + op_webgpu_render_pass_end_occlusion_query(renderPassRid); } /** @@ -3646,7 +4061,7 @@ class GPURenderPassEncoder { }); return rid; }); - ops.op_webgpu_render_pass_execute_bundles(renderPassRid, bundleRids); + op_webgpu_render_pass_execute_bundles(renderPassRid, bundleRids); } end() { @@ -3663,7 +4078,7 @@ class GPURenderPassEncoder { "encoder referenced by this", ); const renderPassRid = assertResource(this, prefix, "this"); - const { err } = ops.op_webgpu_render_pass_end( + const { err } = op_webgpu_render_pass_end( commandEncoderRid, renderPassRid, ); @@ -3695,16 +4110,14 @@ class GPURenderPassEncoder { selfContext: "this", }); if ( - !(ObjectPrototypeIsPrototypeOf( - Uint32ArrayPrototype, - dynamicOffsetsData, - )) + TypedArrayPrototypeGetSymbolToStringTag(dynamicOffsetsData) !== + "Uint32Array" ) { dynamicOffsetsData = new Uint32Array(dynamicOffsetsData ?? []); dynamicOffsetsDataStart = 0; dynamicOffsetsDataLength = dynamicOffsetsData.length; } - ops.op_webgpu_render_pass_set_bind_group( + op_webgpu_render_pass_set_bind_group( renderPassRid, index, bindGroupRid, @@ -3726,7 +4139,7 @@ class GPURenderPassEncoder { assertDevice(this[_encoder], prefix, "encoder referenced by this"); assertResource(this[_encoder], prefix, "encoder referenced by this"); const renderPassRid = assertResource(this, prefix, "this"); - ops.op_webgpu_render_pass_push_debug_group(renderPassRid, groupLabel); + op_webgpu_render_pass_push_debug_group(renderPassRid, groupLabel); } popDebugGroup() { @@ -3736,7 +4149,7 @@ class GPURenderPassEncoder { assertDevice(this[_encoder], prefix, "encoder referenced by this"); assertResource(this[_encoder], prefix, "encoder referenced by this"); const renderPassRid = assertResource(this, prefix, "this"); - ops.op_webgpu_render_pass_pop_debug_group(renderPassRid); + op_webgpu_render_pass_pop_debug_group(renderPassRid); } /** @@ -3755,7 +4168,7 @@ class GPURenderPassEncoder { assertDevice(this[_encoder], prefix, "encoder referenced by this"); assertResource(this[_encoder], prefix, "encoder referenced by this"); const renderPassRid = assertResource(this, prefix, "this"); - ops.op_webgpu_render_pass_insert_debug_marker(renderPassRid, markerLabel); + op_webgpu_render_pass_insert_debug_marker(renderPassRid, markerLabel); } /** @@ -3783,7 +4196,7 @@ class GPURenderPassEncoder { resourceContext: "Argument 1", selfContext: "this", }); - ops.op_webgpu_render_pass_set_pipeline(renderPassRid, pipelineRid); + op_webgpu_render_pass_set_pipeline(renderPassRid, pipelineRid); } /** @@ -3820,7 +4233,7 @@ class GPURenderPassEncoder { resourceContext: "Argument 1", selfContext: "this", }); - ops.op_webgpu_render_pass_set_index_buffer( + op_webgpu_render_pass_set_index_buffer( renderPassRid, bufferRid, indexFormat, @@ -3859,7 +4272,7 @@ class GPURenderPassEncoder { resourceContext: "Argument 2", selfContext: "this", }); - ops.op_webgpu_render_pass_set_vertex_buffer( + op_webgpu_render_pass_set_vertex_buffer( renderPassRid, slot, bufferRid, @@ -3901,7 +4314,7 @@ class GPURenderPassEncoder { assertDevice(this[_encoder], prefix, "encoder referenced by this"); assertResource(this[_encoder], prefix, "encoder referenced by this"); const renderPassRid = assertResource(this, prefix, "this"); - ops.op_webgpu_render_pass_draw( + op_webgpu_render_pass_draw( renderPassRid, vertexCount, instanceCount, @@ -3947,7 +4360,7 @@ class GPURenderPassEncoder { assertDevice(this[_encoder], prefix, "encoder referenced by this"); assertResource(this[_encoder], prefix, "encoder referenced by this"); const renderPassRid = assertResource(this, prefix, "this"); - ops.op_webgpu_render_pass_draw_indexed( + op_webgpu_render_pass_draw_indexed( renderPassRid, indexCount, instanceCount, @@ -3992,7 +4405,7 @@ class GPURenderPassEncoder { resourceContext: "Argument 1", selfContext: "this", }); - ops.op_webgpu_render_pass_draw_indirect( + op_webgpu_render_pass_draw_indirect( renderPassRid, indirectBufferRid, indirectOffset, @@ -4035,7 +4448,7 @@ class GPURenderPassEncoder { resourceContext: "Argument 1", selfContext: "this", }); - ops.op_webgpu_render_pass_draw_indexed_indirect( + op_webgpu_render_pass_draw_indexed_indirect( renderPassRid, indirectBufferRid, indirectOffset, @@ -4046,7 +4459,10 @@ class GPURenderPassEncoder { return inspect( createFilteredInspectProxy({ object: this, - evaluate: ObjectPrototypeIsPrototypeOf(GPURenderPassEncoderPrototype, this), + evaluate: ObjectPrototypeIsPrototypeOf( + GPURenderPassEncoderPrototype, + this, + ), keys: [ "label", ], @@ -4118,7 +4534,7 @@ class GPUComputePassEncoder { resourceContext: "Argument 1", selfContext: "this", }); - ops.op_webgpu_compute_pass_set_pipeline(computePassRid, pipelineRid); + op_webgpu_compute_pass_set_pipeline(computePassRid, pipelineRid); } /** @@ -4153,7 +4569,7 @@ class GPUComputePassEncoder { assertDevice(this[_encoder], prefix, "encoder referenced by this"); assertResource(this[_encoder], prefix, "encoder referenced by this"); const computePassRid = assertResource(this, prefix, "this"); - ops.op_webgpu_compute_pass_dispatch_workgroups( + op_webgpu_compute_pass_dispatch_workgroups( computePassRid, workgroupCountX, workgroupCountY, @@ -4197,7 +4613,7 @@ class GPUComputePassEncoder { resourceContext: "Argument 1", selfContext: "this", }); - ops.op_webgpu_compute_pass_dispatch_workgroups_indirect( + op_webgpu_compute_pass_dispatch_workgroups_indirect( computePassRid, indirectBufferRid, indirectOffset, @@ -4218,7 +4634,7 @@ class GPUComputePassEncoder { "encoder referenced by this", ); const computePassRid = assertResource(this, prefix, "this"); - const { err } = ops.op_webgpu_compute_pass_end( + const { err } = op_webgpu_compute_pass_end( commandEncoderRid, computePassRid, ); @@ -4251,16 +4667,14 @@ class GPUComputePassEncoder { selfContext: "this", }); if ( - !(ObjectPrototypeIsPrototypeOf( - Uint32ArrayPrototype, - dynamicOffsetsData, - )) + TypedArrayPrototypeGetSymbolToStringTag(dynamicOffsetsData) !== + "Uint32Array" ) { dynamicOffsetsData = new Uint32Array(dynamicOffsetsData ?? []); dynamicOffsetsDataStart = 0; dynamicOffsetsDataLength = dynamicOffsetsData.length; } - ops.op_webgpu_compute_pass_set_bind_group( + op_webgpu_compute_pass_set_bind_group( computePassRid, index, bindGroupRid, @@ -4282,7 +4696,7 @@ class GPUComputePassEncoder { assertDevice(this[_encoder], prefix, "encoder referenced by this"); assertResource(this[_encoder], prefix, "encoder referenced by this"); const computePassRid = assertResource(this, prefix, "this"); - ops.op_webgpu_compute_pass_push_debug_group(computePassRid, groupLabel); + op_webgpu_compute_pass_push_debug_group(computePassRid, groupLabel); } popDebugGroup() { @@ -4292,7 +4706,7 @@ class GPUComputePassEncoder { assertDevice(this[_encoder], prefix, "encoder referenced by this"); assertResource(this[_encoder], prefix, "encoder referenced by this"); const computePassRid = assertResource(this, prefix, "this"); - ops.op_webgpu_compute_pass_pop_debug_group(computePassRid); + op_webgpu_compute_pass_pop_debug_group(computePassRid); } /** @@ -4311,7 +4725,7 @@ class GPUComputePassEncoder { assertDevice(this[_encoder], prefix, "encoder referenced by this"); assertResource(this[_encoder], prefix, "encoder referenced by this"); const computePassRid = assertResource(this, prefix, "this"); - ops.op_webgpu_compute_pass_insert_debug_marker( + op_webgpu_compute_pass_insert_debug_marker( computePassRid, markerLabel, ); @@ -4321,7 +4735,10 @@ class GPUComputePassEncoder { return inspect( createFilteredInspectProxy({ object: this, - evaluate: ObjectPrototypeIsPrototypeOf(GPUComputePassEncoderPrototype, this), + evaluate: ObjectPrototypeIsPrototypeOf( + GPUComputePassEncoderPrototype, + this, + ), keys: [ "label", ], @@ -4430,7 +4847,7 @@ class GPURenderBundleEncoder { ); const device = assertDevice(this, prefix, "this"); const renderBundleEncoderRid = assertResource(this, prefix, "this"); - const { rid, err } = ops.op_webgpu_render_bundle_encoder_finish( + const { rid, err } = op_webgpu_render_bundle_encoder_finish( renderBundleEncoderRid, descriptor.label, ); @@ -4466,16 +4883,14 @@ class GPURenderBundleEncoder { selfContext: "this", }); if ( - !(ObjectPrototypeIsPrototypeOf( - Uint32ArrayPrototype, - dynamicOffsetsData, - )) + TypedArrayPrototypeGetSymbolToStringTag(dynamicOffsetsData) !== + "Uint32Array" ) { dynamicOffsetsData = new Uint32Array(dynamicOffsetsData ?? []); dynamicOffsetsDataStart = 0; dynamicOffsetsDataLength = dynamicOffsetsData.length; } - ops.op_webgpu_render_bundle_encoder_set_bind_group( + op_webgpu_render_bundle_encoder_set_bind_group( renderBundleEncoderRid, index, bindGroupRid, @@ -4496,7 +4911,7 @@ class GPURenderBundleEncoder { groupLabel = webidl.converters.USVString(groupLabel, prefix, "Argument 1"); assertDevice(this, prefix, "this"); const renderBundleEncoderRid = assertResource(this, prefix, "this"); - ops.op_webgpu_render_bundle_encoder_push_debug_group( + op_webgpu_render_bundle_encoder_push_debug_group( renderBundleEncoderRid, groupLabel, ); @@ -4508,7 +4923,7 @@ class GPURenderBundleEncoder { "Failed to execute 'popDebugGroup' on 'GPURenderBundleEncoder'"; assertDevice(this, prefix, "this"); const renderBundleEncoderRid = assertResource(this, prefix, "this"); - ops.op_webgpu_render_bundle_encoder_pop_debug_group( + op_webgpu_render_bundle_encoder_pop_debug_group( renderBundleEncoderRid, ); } @@ -4528,7 +4943,7 @@ class GPURenderBundleEncoder { ); assertDevice(this, prefix, "this"); const renderBundleEncoderRid = assertResource(this, prefix, "this"); - ops.op_webgpu_render_bundle_encoder_insert_debug_marker( + op_webgpu_render_bundle_encoder_insert_debug_marker( renderBundleEncoderRid, markerLabel, ); @@ -4555,7 +4970,7 @@ class GPURenderBundleEncoder { resourceContext: "Argument 1", selfContext: "this", }); - ops.op_webgpu_render_bundle_encoder_set_pipeline( + op_webgpu_render_bundle_encoder_set_pipeline( renderBundleEncoderRid, pipelineRid, ); @@ -4588,7 +5003,7 @@ class GPURenderBundleEncoder { resourceContext: "Argument 1", selfContext: "this", }); - ops.op_webgpu_render_bundle_encoder_set_index_buffer( + op_webgpu_render_bundle_encoder_set_index_buffer( renderBundleEncoderRid, bufferRid, indexFormat, @@ -4622,7 +5037,7 @@ class GPURenderBundleEncoder { resourceContext: "Argument 2", selfContext: "this", }); - ops.op_webgpu_render_bundle_encoder_set_vertex_buffer( + op_webgpu_render_bundle_encoder_set_vertex_buffer( renderBundleEncoderRid, slot, bufferRid, @@ -4663,7 +5078,7 @@ class GPURenderBundleEncoder { ); assertDevice(this, prefix, "this"); const renderBundleEncoderRid = assertResource(this, prefix, "this"); - ops.op_webgpu_render_bundle_encoder_draw( + op_webgpu_render_bundle_encoder_draw( renderBundleEncoderRid, vertexCount, instanceCount, @@ -4709,7 +5124,7 @@ class GPURenderBundleEncoder { ); assertDevice(this, prefix, "this"); const renderBundleEncoderRid = assertResource(this, prefix, "this"); - ops.op_webgpu_render_bundle_encoder_draw_indexed( + op_webgpu_render_bundle_encoder_draw_indexed( renderBundleEncoderRid, indexCount, instanceCount, @@ -4750,7 +5165,7 @@ class GPURenderBundleEncoder { resourceContext: "Argument 1", selfContext: "this", }); - ops.op_webgpu_render_bundle_encoder_draw_indirect( + op_webgpu_render_bundle_encoder_draw_indirect( renderBundleEncoderRid, indirectBufferRid, indirectOffset, @@ -4761,7 +5176,10 @@ class GPURenderBundleEncoder { return inspect( createFilteredInspectProxy({ object: this, - evaluate: ObjectPrototypeIsPrototypeOf(GPURenderBundleEncoderPrototype, this), + evaluate: ObjectPrototypeIsPrototypeOf( + GPURenderBundleEncoderPrototype, + this, + ), keys: [ "label", ], @@ -4870,12 +5288,12 @@ class GPUQuerySet { get type() { webidl.assertBranded(this, GPUQuerySetPrototype); - this[_type](); + return this[_type](); } get count() { webidl.assertBranded(this, GPUQuerySetPrototype); - this[_count](); + return this[_count](); } [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) { @@ -5014,6 +5432,27 @@ webidl.converters["GPUFeatureName"] = webidl.createEnumConverter( ], ); +// DICTIONARY: GPUPipelineErrorInit +webidl.converters["GPUPipelineErrorInit"] = webidl.createDictionaryConverter( + "GPUPipelineErrorInit", + [ + { + key: "reason", + converter: webidl.converters.GPUPipelineErrorReason, + required: true, + }, + ], +); + +// ENUM: GPUPipelineErrorReason +webidl.converters["GPUPipelineErrorReason"] = webidl.createEnumConverter( + "GPUPipelineErrorReason", + [ + "validation", + "internal", + ], +); + // TYPEDEF: GPUSize32 webidl.converters["GPUSize32"] = (V, opts) => webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }); @@ -5193,6 +5632,7 @@ webidl.converters["GPUTextureFormat"] = webidl.createEnumConverter( "bgra8unorm", "bgra8unorm-srgb", "rgb9e5ufloat", + "rgb10a2uint", "rgb10a2unorm", "rg11b10ufloat", "rg32uint", @@ -5608,6 +6048,8 @@ webidl.converters["GPUStorageTextureAccess"] = webidl.createEnumConverter( "GPUStorageTextureAccess", [ "write-only", + "read-only", + "read-write", ], ); @@ -5867,7 +6309,6 @@ const dictMembersGPUProgrammableStage = [ { key: "entryPoint", converter: webidl.converters["USVString"], - required: true, }, { key: "constants", @@ -6910,7 +7351,7 @@ webidl.converters["GPUErrorFilter"] = webidl.createEnumConverter( [ "out-of-memory", "validation", - "internal" + "internal", ], ); @@ -6957,6 +7398,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, @@ -6978,6 +7491,7 @@ export { GPUDevice, GPUDeviceLostInfo, GPUError, + GPUInternalError, GPUMapMode, GPUOutOfMemoryError, GPUPipelineLayout, @@ -6995,5 +7509,6 @@ export { GPUTexture, GPUTextureUsage, GPUTextureView, + GPUUncapturedErrorEvent, GPUValidationError, }; diff --git a/deno_webgpu/02_surface.js b/deno_webgpu/02_surface.js index d16f5c245..f35f745af 100644 --- a/deno_webgpu/02_surface.js +++ b/deno_webgpu/02_surface.js @@ -1,4 +1,4 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // @ts-check /// @@ -6,27 +6,32 @@ /// /// -const core = globalThis.Deno.core; -const ops = core.ops; -import * as webidl from "ext:deno_webidl/00_webidl.js"; -const primordials = globalThis.__bootstrap.primordials; -const { Symbol } = primordials; +import { primordials } from "ext:core/mod.js"; import { - _device, - assertDevice, - createGPUTexture, - GPUTextureUsage, -} from "ext:deno_webgpu/01_webgpu.js"; + op_webgpu_surface_configure, + op_webgpu_surface_create, + op_webgpu_surface_get_current_texture, + op_webgpu_surface_present, +} from "ext:core/ops"; +const { + ObjectPrototypeIsPrototypeOf, + Symbol, + SymbolFor, + TypeError, +} = primordials; + +import * as webidl from "ext:deno_webidl/00_webidl.js"; +import { createFilteredInspectProxy } from "ext:deno_console/01_console.js"; +import { loadWebGPU } 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,11 +55,15 @@ class GPUCanvasContext { context: "Argument 1", }); + const { _device, assertDevice } = loadWebGPU(); this[_device] = configuration.device[_device]; this[_configuration] = configuration; - const device = assertDevice(this, { prefix, context: "configuration.device" }); + const device = assertDevice(this, { + prefix, + context: "configuration.device", + }); - const { err } = ops.op_webgpu_surface_configure({ + const { err } = op_webgpu_surface_configure({ surfaceRid: this[_surfaceRid], deviceRid: device.rid, format: configuration.format, @@ -69,6 +78,8 @@ class GPUCanvasContext { } unconfigure() { + const { _device } = loadWebGPU(); + webidl.assertBranded(this, GPUCanvasContextPrototype); this[_configuration] = null; @@ -77,11 +88,13 @@ class GPUCanvasContext { getCurrentTexture() { webidl.assertBranded(this, GPUCanvasContextPrototype); - const prefix = "Failed to execute 'getCurrentTexture' on 'GPUCanvasContext'"; + const prefix = + "Failed to execute 'getCurrentTexture' on 'GPUCanvasContext'"; if (this[_configuration] === null) { throw new DOMException("context is not configured.", "InvalidStateError"); } + const { createGPUTexture, assertDevice } = loadWebGPU(); const device = assertDevice(this, { prefix, context: "this" }); @@ -89,7 +102,10 @@ class GPUCanvasContext { return this[_currentTexture]; } - const { rid } = ops.op_webgpu_surface_get_current_texture(device.rid, this[_surfaceRid]); + const { rid } = op_webgpu_surface_get_current_texture( + device.rid, + this[_surfaceRid], + ); const texture = createGPUTexture( { @@ -112,102 +128,66 @@ 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 } = loadWebGPU(); + 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]); + const device = assertDevice(this[_currentTexture], { + prefix, + context: "this", + }); + op_webgpu_surface_present(device.rid, this[_surfaceRid]); this[_currentTexture].destroy(); this[_currentTexture] = undefined; } + + [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) { + return inspect( + createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf(GPUCanvasContextPrototype, this), + keys: [ + "canvas", + ], + }), + inspectOptions, + ); + } } const GPUCanvasContextPrototype = GPUCanvasContext.prototype; function createCanvasContext(options) { + // lazy load webgpu if needed const canvasContext = webidl.createBranded(GPUCanvasContext); canvasContext[_surfaceRid] = options.surfaceRid; canvasContext[_canvas] = options.canvas; return canvasContext; } -// Converters +// External webgpu surfaces -// ENUM: GPUCanvasAlphaMode -webidl.converters["GPUCanvasAlphaMode"] = webidl.createEnumConverter( - "GPUCanvasAlphaMode", - [ - "opaque", - "premultiplied", - ], -); +// TODO(@littledivy): This will extend `OffscreenCanvas` when we add it. +class UnsafeWindowSurface { + #ctx; + #surfaceRid; -// NON-SPEC: ENUM: GPUPresentMode -webidl.converters["GPUPresentMode"] = webidl.createEnumConverter( - "GPUPresentMode", - [ - "autoVsync", - "autoNoVsync", - "fifo", - "fifoRelaxed", - "immediate", - "mailbox", - ], -); + constructor(system, win, display) { + this.#surfaceRid = op_webgpu_surface_create(system, win, display); + } -// 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", - }, + getContext(context) { + if (context !== "webgpu") { + throw new TypeError("Only 'webgpu' context is supported."); + } + this.#ctx = createCanvasContext({ surfaceRid: this.#surfaceRid }); + return this.#ctx; + } - // 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, - ); + present() { + this.#ctx[_present](); + } +} - -window.__bootstrap.webgpu = { - ...window.__bootstrap.webgpu, - GPUCanvasContext, - createCanvasContext, -}; +export { GPUCanvasContext, UnsafeWindowSurface }; diff --git a/deno_webgpu/Cargo.toml b/deno_webgpu/Cargo.toml index 9f6d96a95..586eb90c8 100644 --- a/deno_webgpu/Cargo.toml +++ b/deno_webgpu/Cargo.toml @@ -1,8 +1,8 @@ -# Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. [package] name = "deno_webgpu" -version = "0.85.0" +version = "0.110.0" authors = ["the Deno authors"] edition.workspace = true license = "MIT" @@ -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] @@ -23,11 +20,11 @@ deno_core.workspace = true serde = { workspace = true, features = ["derive"] } tokio = { workspace = true, features = ["full"] } wgpu-types = { workspace = true, features = ["serde"] } -raw-window-handle = { workspace = true, optional = true } +raw-window-handle = { workspace = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies.wgpu-core] workspace = true -features = ["trace", "replay", "serde", "strict_asserts", "wgsl", "gles"] +features = ["raw-window-handle", "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] diff --git a/deno_webgpu/LICENSE.md b/deno_webgpu/LICENSE.md index aec557f3a..56753af36 100644 --- a/deno_webgpu/LICENSE.md +++ b/deno_webgpu/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright 2018-2023 the Deno authors +Copyright 2018-2024 the Deno authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/deno_webgpu/README.md b/deno_webgpu/README.md index 1cf031cda..c419bfc60 100644 --- a/deno_webgpu/README.md +++ b/deno_webgpu/README.md @@ -2,8 +2,8 @@ This op crate implements the WebGPU API as defined in https://gpuweb.github.io/gpuweb/ in Deno. The implementation targets the spec -draft as of February 22, 2021. The spec is still very much in flux. This op -crate tries to stay up to date with the spec, but is constrained by the features +draft as of March 31, 2024. The spec is still very much in flux. This extension +tries to stay up to date with the spec, but is constrained by the features implemented in our GPU backend library [wgpu](https://github.com/gfx-rs/wgpu). The spec is still very bare bones, and is still missing many details. As the diff --git a/deno_webgpu/binding.rs b/deno_webgpu/binding.rs index c0b9b5836..0efeb6716 100644 --- a/deno_webgpu/binding.rs +++ b/deno_webgpu/binding.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use deno_core::error::AnyError; use deno_core::op2; @@ -112,25 +112,11 @@ impl From for wgpu_types::TextureSampleType { #[derive(Deserialize)] #[serde(rename_all = "camelCase")] struct GpuStorageTextureBindingLayout { - access: GpuStorageTextureAccess, + access: wgpu_types::StorageTextureAccess, format: wgpu_types::TextureFormat, view_dimension: wgpu_types::TextureViewDimension, } -#[derive(Deserialize)] -#[serde(rename_all = "kebab-case")] -enum GpuStorageTextureAccess { - WriteOnly, -} - -impl From for wgpu_types::StorageTextureAccess { - fn from(access: GpuStorageTextureAccess) -> Self { - match access { - GpuStorageTextureAccess::WriteOnly => wgpu_types::StorageTextureAccess::WriteOnly, - } - } -} - #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct GpuBindGroupLayoutEntry { @@ -165,7 +151,7 @@ impl From for wgpu_types::BindingType { }, GpuBindingType::StorageTexture(storage_texture) => { wgpu_types::BindingType::StorageTexture { - access: storage_texture.access.into(), + access: storage_texture.access, format: storage_texture.format, view_dimension: storage_texture.view_dimension, } diff --git a/deno_webgpu/buffer.rs b/deno_webgpu/buffer.rs index 9a2ebb003..5b7d20880 100644 --- a/deno_webgpu/buffer.rs +++ b/deno_webgpu/buffer.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use deno_core::error::type_error; use deno_core::error::AnyError; @@ -163,6 +163,7 @@ pub fn op_webgpu_buffer_get_mapped_range( )) .map_err(|e| DomExceptionOperationError::new(&e.to_string()))?; + // SAFETY: guarantee to be safe from wgpu let slice = unsafe { std::slice::from_raw_parts_mut(slice_pointer, range_size as usize) }; buf.copy_from_slice(slice); @@ -189,6 +190,7 @@ pub fn op_webgpu_buffer_unmap( let buffer = buffer_resource.1; if let Some(buf) = buf { + // SAFETY: guarantee to be safe from wgpu let slice = unsafe { std::slice::from_raw_parts_mut(mapped_resource.0, mapped_resource.1) }; slice.copy_from_slice(buf); } diff --git a/deno_webgpu/bundle.rs b/deno_webgpu/bundle.rs index d50359931..dfe5ccf49 100644 --- a/deno_webgpu/bundle.rs +++ b/deno_webgpu/bundle.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use deno_core::error::type_error; use deno_core::error::AnyError; diff --git a/deno_webgpu/byow.rs b/deno_webgpu/byow.rs new file mode 100644 index 000000000..3042f46ee --- /dev/null +++ b/deno_webgpu/byow.rs @@ -0,0 +1,115 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use deno_core::error::type_error; +use deno_core::error::AnyError; +use deno_core::op2; +use deno_core::OpState; +use deno_core::ResourceId; +use std::ffi::c_void; +use std::ptr::NonNull; + +use crate::surface::WebGpuSurface; + +#[op2(fast)] +#[smi] +pub fn op_webgpu_surface_create( + state: &mut OpState, + #[string] system: &str, + p1: *const c_void, + p2: *const c_void, +) -> Result { + let instance = state.borrow::(); + // Security note: + // + // The `p1` and `p2` parameters are pointers to platform-specific window + // handles. + // + // The code below works under the assumption that: + // + // - handles can only be created by the FFI interface which + // enforces --allow-ffi. + // + // - `*const c_void` deserizalizes null and v8::External. + // + // - Only FFI can export v8::External to user code. + if p1.is_null() { + return Err(type_error("Invalid parameters")); + } + + let (win_handle, display_handle) = raw_window(system, p1, p2)?; + let surface = unsafe { instance.instance_create_surface(display_handle, win_handle, None)? }; + + let rid = state + .resource_table + .add(WebGpuSurface(instance.clone(), surface)); + Ok(rid) +} + +type RawHandles = ( + raw_window_handle::RawWindowHandle, + raw_window_handle::RawDisplayHandle, +); + +#[cfg(target_os = "macos")] +fn raw_window( + system: &str, + _ns_window: *const c_void, + ns_view: *const c_void, +) -> Result { + if system != "cocoa" { + return Err(type_error("Invalid system on macOS")); + } + + let win_handle = + raw_window_handle::RawWindowHandle::AppKit(raw_window_handle::AppKitWindowHandle::new( + NonNull::new(ns_view as *mut c_void).ok_or(type_error("ns_view is null"))?, + )); + + let display_handle = + raw_window_handle::RawDisplayHandle::AppKit(raw_window_handle::AppKitDisplayHandle::new()); + Ok((win_handle, display_handle)) +} + +#[cfg(target_os = "windows")] +fn raw_window( + system: &str, + window: *const c_void, + hinstance: *const c_void, +) -> Result { + use raw_window_handle::WindowsDisplayHandle; + if system != "win32" { + return Err(type_error("Invalid system on Windows")); + } + + let win_handle = { + let mut handle = raw_window_handle::Win32WindowHandle::new(); + handle.hwnd = window as *mut c_void; + handle.hinstance = hinstance as *mut c_void; + + raw_window_handle::RawWindowHandle::Win32(handle) + }; + + let display_handle = raw_window_handle::RawDisplayHandle::Windows(WindowsDisplayHandle::new()); + Ok((win_handle, display_handle)) +} + +#[cfg(target_os = "linux")] +fn raw_window( + system: &str, + window: *const c_void, + display: *const c_void, +) -> Result { + if system != "x11" { + return Err(type_error("Invalid system on Linux")); + } + + let win_handle = raw_window_handle::RawWindowHandle::Xlib( + raw_window_handle::XlibWindowHandle::new(window as *mut c_void as _), + ); + + let display_handle = raw_window_handle::RawDisplayHandle::Xlib( + raw_window_handle::XlibDisplayHandle::new(NonNull::new(display as *mut c_void), 0), + ); + + Ok((win_handle, display_handle)) +} diff --git a/deno_webgpu/command_encoder.rs b/deno_webgpu/command_encoder.rs index 679ac3cab..20dfe0db0 100644 --- a/deno_webgpu/command_encoder.rs +++ b/deno_webgpu/command_encoder.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use crate::WebGpuQuerySet; use deno_core::error::AnyError; diff --git a/deno_webgpu/compute_pass.rs b/deno_webgpu/compute_pass.rs index e1c9e2919..65ac93d63 100644 --- a/deno_webgpu/compute_pass.rs +++ b/deno_webgpu/compute_pass.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use deno_core::error::AnyError; use deno_core::op2; diff --git a/deno_webgpu/error.rs b/deno_webgpu/error.rs index 6c509a80d..bb8200899 100644 --- a/deno_webgpu/error.rs +++ b/deno_webgpu/error.rs @@ -1,4 +1,5 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + use deno_core::error::AnyError; use deno_core::ResourceId; use serde::Serialize; @@ -23,7 +24,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; @@ -87,6 +87,7 @@ pub enum WebGpuError { Lost, OutOfMemory, Validation(String), + Internal, } impl From for WebGpuError { @@ -277,7 +278,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/deno_webgpu/lib.rs b/deno_webgpu/lib.rs index 40e76e0fa..453d4ea7e 100644 --- a/deno_webgpu/lib.rs +++ b/deno_webgpu/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. #![cfg(not(target_arch = "wasm32"))] #![warn(unsafe_op_in_unsafe_fn)] @@ -19,6 +19,8 @@ pub use wgpu_types; use error::DomExceptionOperationError; use error::WebGpuResult; +pub const UNSTABLE_FEATURE_NAME: &str = "webgpu"; + #[macro_use] mod macros { macro_rules! gfx_select { @@ -71,6 +73,7 @@ mod macros { pub mod binding; pub mod buffer; pub mod bundle; +pub mod byow; pub mod command_encoder; pub mod compute_pass; pub mod error; @@ -79,23 +82,9 @@ 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); - -fn check_unstable(state: &OpState, api_name: &str) { - let unstable = state.borrow::(); - if !unstable.0 { - eprintln!( - "Unstable API '{}'. The --unstable flag must be provided.", - api_name - ); - std::process::exit(70); - } -} - pub type Instance = std::sync::Arc; struct WebGpuAdapter(Instance, wgpu_core::id::AdapterId); @@ -224,12 +213,15 @@ 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, + // byow + byow::op_webgpu_surface_create, ], - esm = ["01_webgpu.js"], - options = { unstable: bool }, - state = |state, options| { - state.put(Unstable(options.unstable)); - }, + esm = ["00_init.js", "02_surface.js"], + lazy_loaded_esm = ["01_webgpu.js"], ); fn deserialize_features(features: &wgpu_types::Features) -> Vec<&'static str> { @@ -377,29 +369,45 @@ fn deserialize_features(features: &wgpu_types::Features) -> Vec<&'static str> { #[derive(Serialize)] #[serde(untagged)] -pub enum GpuAdapterDeviceOrErr { +pub enum GpuAdapterResOrErr { Error { err: String }, - Features(GpuAdapterDevice), + Features(GpuAdapterRes), } #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub struct GpuAdapterDevice { +pub struct GpuAdapterRes { rid: ResourceId, limits: wgpu_types::Limits, features: Vec<&'static str>, is_software: bool, } -#[op2(async)] +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct GpuDeviceRes { + rid: ResourceId, + queue_rid: ResourceId, + limits: wgpu_types::Limits, + features: Vec<&'static str>, + is_software: bool, +} + +#[op2] #[serde] -pub async fn op_webgpu_request_adapter( +pub fn op_webgpu_request_adapter( state: Rc>, #[serde] power_preference: Option, force_fallback_adapter: bool, -) -> Result { +) -> Result { let mut state = state.borrow_mut(); - check_unstable(&state, "navigator.gpu.requestAdapter"); + + // TODO(bartlomieju): replace with `state.feature_checker.check_or_exit` + // once we phase out `check_or_exit_with_legacy_fallback` + state + .feature_checker + .check_or_exit_with_legacy_fallback(UNSTABLE_FEATURE_NAME, "navigator.gpu.requestAdapter"); + let backends = std::env::var("DENO_WEBGPU_BACKEND").map_or_else( |_| wgpu_types::Backends::all(), |s| wgpu_core::instance::parse_backends_from_comma_list(&s), @@ -432,7 +440,7 @@ pub async fn op_webgpu_request_adapter( let adapter = match res { Ok(adapter) => adapter, Err(err) => { - return Ok(GpuAdapterDeviceOrErr::Error { + return Ok(GpuAdapterResOrErr::Error { err: err.to_string(), }) } @@ -445,7 +453,7 @@ pub async fn op_webgpu_request_adapter( let rid = state.resource_table.add(WebGpuAdapter(instance, adapter)); - Ok(GpuAdapterDeviceOrErr::Features(GpuAdapterDevice { + Ok(GpuAdapterResOrErr::Features(GpuAdapterRes { rid, features, limits: adapter_limits, @@ -649,15 +657,15 @@ impl From for wgpu_types::Features { } } -#[op2(async)] +#[op2] #[serde] -pub async fn op_webgpu_request_device( +pub fn op_webgpu_request_device( state: Rc>, #[smi] adapter_rid: ResourceId, #[string] label: String, #[serde] required_features: GpuRequiredFeatures, #[serde] required_limits: Option, -) -> Result { +) -> Result { let mut state = state.borrow_mut(); let adapter_resource = state.resource_table.get::(adapter_rid)?; let adapter = adapter_resource.1; @@ -669,7 +677,7 @@ pub async fn op_webgpu_request_device( required_limits: required_limits.unwrap_or_default(), }; - let (device, _queue, maybe_err) = gfx_select!(adapter => instance.adapter_request_device( + let (device, queue, maybe_err) = gfx_select!(adapter => instance.adapter_request_device( adapter, &descriptor, std::env::var("DENO_WEBGPU_TRACE").ok().as_ref().map(std::path::Path::new), @@ -685,10 +693,15 @@ pub async fn op_webgpu_request_device( let limits = gfx_select!(device => instance.device_limits(device))?; let instance = instance.clone(); + let instance2 = instance.clone(); let rid = state.resource_table.add(WebGpuDevice(instance, device)); + let queue_rid = state + .resource_table + .add(queue::WebGpuQueue(instance2, queue)); - Ok(GpuAdapterDevice { + Ok(GpuDeviceRes { rid, + queue_rid, features, limits, // TODO(lucacasonato): report correctly from wgpu @@ -705,9 +718,9 @@ pub struct GPUAdapterInfo { description: String, } -#[op2(async)] +#[op2] #[serde] -pub async fn op_webgpu_request_adapter_info( +pub fn op_webgpu_request_adapter_info( state: Rc>, #[smi] adapter_rid: ResourceId, ) -> Result { diff --git a/deno_webgpu/pipeline.rs b/deno_webgpu/pipeline.rs index dcd4151eb..ab7cf42e7 100644 --- a/deno_webgpu/pipeline.rs +++ b/deno_webgpu/pipeline.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use deno_core::error::AnyError; use deno_core::op2; @@ -74,7 +74,7 @@ pub enum GPUPipelineLayoutOrGPUAutoLayoutMode { #[serde(rename_all = "camelCase")] pub struct GpuProgrammableStage { module: ResourceId, - entry_point: String, + entry_point: Option, // constants: HashMap } @@ -110,7 +110,7 @@ pub fn op_webgpu_create_compute_pipeline( layout: pipeline_layout, stage: wgpu_core::pipeline::ProgrammableStageDescriptor { module: compute_shader_module_resource.1, - entry_point: Some(Cow::from(compute.entry_point)), + entry_point: compute.entry_point.map(Cow::from), // TODO(lucacasonato): support args.compute.constants }, }; diff --git a/deno_webgpu/queue.rs b/deno_webgpu/queue.rs index 61b56c758..264013445 100644 --- a/deno_webgpu/queue.rs +++ b/deno_webgpu/queue.rs @@ -1,16 +1,28 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use crate::command_encoder::WebGpuCommandBuffer; +use crate::Instance; use deno_core::error::AnyError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; use serde::Deserialize; +use std::borrow::Cow; +use std::rc::Rc; use super::error::WebGpuResult; -type WebGpuQueue = super::WebGpuDevice; +pub struct WebGpuQueue(pub Instance, pub wgpu_core::id::QueueId); +impl Resource for WebGpuQueue { + fn name(&self) -> Cow { + "webGPUQueue".into() + } + + fn close(self: Rc) { + gfx_select!(self.1 => self.0.queue_drop(self.1)); + } +} #[op2] #[serde] @@ -19,7 +31,7 @@ pub fn op_webgpu_queue_submit( #[smi] queue_rid: ResourceId, #[serde] command_buffers: Vec, ) -> Result { - let instance = state.borrow::(); + let instance = state.borrow::(); let queue_resource = state.resource_table.get::(queue_rid)?; let queue = queue_resource.1; @@ -32,7 +44,7 @@ pub fn op_webgpu_queue_submit( }) .collect::, AnyError>>()?; - let maybe_err = gfx_select!(queue => instance.queue_submit(queue.transmute(), &ids)).err(); + let maybe_err = gfx_select!(queue => instance.queue_submit(queue, &ids)).err(); for rid in command_buffers { let resource = state.resource_table.take::(rid)?; @@ -71,7 +83,7 @@ pub fn op_webgpu_write_buffer( #[number] size: Option, #[buffer] buf: &[u8], ) -> Result { - let instance = state.borrow::(); + let instance = state.borrow::(); let buffer_resource = state .resource_table .get::(buffer)?; @@ -84,7 +96,7 @@ pub fn op_webgpu_write_buffer( None => &buf[data_offset..], }; let maybe_err = gfx_select!(queue => instance.queue_write_buffer( - queue.transmute(), + queue, buffer, buffer_offset, data @@ -104,7 +116,7 @@ pub fn op_webgpu_write_texture( #[serde] size: wgpu_types::Extent3d, #[buffer] buf: &[u8], ) -> Result { - let instance = state.borrow::(); + let instance = state.borrow::(); let texture_resource = state .resource_table .get::(destination.texture)?; @@ -120,7 +132,7 @@ pub fn op_webgpu_write_texture( let data_layout = data_layout.into(); gfx_ok!(queue => instance.queue_write_texture( - queue.transmute(), + queue, &destination, buf, &data_layout, diff --git a/deno_webgpu/render_pass.rs b/deno_webgpu/render_pass.rs index 47b98c91f..11b2f2286 100644 --- a/deno_webgpu/render_pass.rs +++ b/deno_webgpu/render_pass.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use deno_core::error::type_error; use deno_core::error::AnyError; diff --git a/deno_webgpu/sampler.rs b/deno_webgpu/sampler.rs index 0d65d727a..822c4bda1 100644 --- a/deno_webgpu/sampler.rs +++ b/deno_webgpu/sampler.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use deno_core::error::AnyError; use deno_core::op2; diff --git a/deno_webgpu/shader.rs b/deno_webgpu/shader.rs index f4604a04a..17cde4393 100644 --- a/deno_webgpu/shader.rs +++ b/deno_webgpu/shader.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use deno_core::error::AnyError; use deno_core::op2; diff --git a/deno_webgpu/surface.rs b/deno_webgpu/surface.rs index 4d8412999..a8b984eef 100644 --- a/deno_webgpu/surface.rs +++ b/deno_webgpu/surface.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use super::WebGpuResult; use deno_core::error::AnyError; @@ -11,21 +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"], - options = { unstable: bool }, - state = |state, options| { - state.put(super::Unstable(options.unstable)); - }, -); - pub struct WebGpuSurface(pub crate::Instance, pub wgpu_core::id::SurfaceId); impl Resource for WebGpuSurface { fn name(&self) -> Cow { diff --git a/deno_webgpu/texture.rs b/deno_webgpu/texture.rs index a9be7b991..2dc1a740a 100644 --- a/deno_webgpu/texture.rs +++ b/deno_webgpu/texture.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use deno_core::error::AnyError; use deno_core::op2; diff --git a/deno_webgpu/webgpu.idl b/deno_webgpu/webgpu.idl index 46d587874..bd709d117 100644 --- a/deno_webgpu/webgpu.idl +++ b/deno_webgpu/webgpu.idl @@ -6,7 +6,7 @@ dictionary GPUObjectDescriptorBase { USVString label = ""; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUSupportedLimits { readonly attribute unsigned long maxTextureDimension1D; readonly attribute unsigned long maxTextureDimension2D; @@ -30,6 +30,8 @@ interface GPUSupportedLimits { readonly attribute unsigned long maxVertexAttributes; readonly attribute unsigned long maxVertexBufferArrayStride; readonly attribute unsigned long maxInterStageShaderComponents; + readonly attribute unsigned long maxColorAttachments; + readonly attribute unsigned long maxColorAttachmentBytesPerSample; readonly attribute unsigned long maxComputeWorkgroupStorageSize; readonly attribute unsigned long maxComputeInvocationsPerWorkgroup; readonly attribute unsigned long maxComputeWorkgroupSizeX; @@ -38,12 +40,12 @@ interface GPUSupportedLimits { readonly attribute unsigned long maxComputeWorkgroupsPerDimension; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUSupportedFeatures { readonly setlike; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUAdapterInfo { readonly attribute DOMString vendor; readonly attribute DOMString architecture; @@ -57,9 +59,10 @@ interface mixin NavigatorGPU { Navigator includes NavigatorGPU; WorkerNavigator includes NavigatorGPU; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPU { Promise requestAdapter(optional GPURequestAdapterOptions options = {}); + GPUTextureFormat getPreferredCanvasFormat(); }; dictionary GPURequestAdapterOptions { @@ -72,14 +75,14 @@ enum GPUPowerPreference { "high-performance", }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUAdapter { [SameObject] readonly attribute GPUSupportedFeatures features; [SameObject] readonly attribute GPUSupportedLimits limits; readonly attribute boolean isFallbackAdapter; Promise requestDevice(optional GPUDeviceDescriptor descriptor = {}); - Promise requestAdapterInfo(optional sequence unmaskHints = []); + Promise requestAdapterInfo(); }; dictionary GPUDeviceDescriptor @@ -141,7 +144,7 @@ enum GPUFeatureName { "shader-early-depth-test", }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUDevice : EventTarget { [SameObject] readonly attribute GPUSupportedFeatures features; [SameObject] readonly attribute GPUSupportedLimits limits; @@ -171,7 +174,7 @@ interface GPUDevice : EventTarget { }; GPUDevice includes GPUObjectBase; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUBuffer { readonly attribute GPUSize64Out size; readonly attribute GPUFlagsConstant usage; @@ -200,7 +203,7 @@ dictionary GPUBufferDescriptor }; typedef [EnforceRange] unsigned long GPUBufferUsageFlags; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] namespace GPUBufferUsage { const GPUFlagsConstant MAP_READ = 0x0001; const GPUFlagsConstant MAP_WRITE = 0x0002; @@ -215,13 +218,13 @@ namespace GPUBufferUsage { }; typedef [EnforceRange] unsigned long GPUMapModeFlags; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] namespace GPUMapMode { const GPUFlagsConstant READ = 0x0001; const GPUFlagsConstant WRITE = 0x0002; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUTexture { GPUTextureView createView(optional GPUTextureViewDescriptor descriptor = {}); @@ -256,7 +259,7 @@ enum GPUTextureDimension { }; typedef [EnforceRange] unsigned long GPUTextureUsageFlags; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] namespace GPUTextureUsage { const GPUFlagsConstant COPY_SRC = 0x01; const GPUFlagsConstant COPY_DST = 0x02; @@ -265,7 +268,7 @@ namespace GPUTextureUsage { const GPUFlagsConstant RENDER_ATTACHMENT = 0x10; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUTextureView { }; GPUTextureView includes GPUObjectBase; @@ -328,6 +331,7 @@ enum GPUTextureFormat { "bgra8unorm-srgb", // Packed 32-bit formats "rgb9e5ufloat", + "rgb10a2uint", "rgb10a2unorm", "rg11b10ufloat", @@ -416,7 +420,7 @@ enum GPUTextureFormat { "astc-12x12-unorm-srgb", }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUSampler { }; GPUSampler includes GPUObjectBase; @@ -462,7 +466,7 @@ enum GPUCompareFunction { "always", }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUBindGroupLayout { }; GPUBindGroupLayout includes GPUObjectBase; @@ -483,7 +487,7 @@ dictionary GPUBindGroupLayoutEntry { }; typedef [EnforceRange] unsigned long GPUShaderStageFlags; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] namespace GPUShaderStage { const GPUFlagsConstant VERTEX = 0x1; const GPUFlagsConstant FRAGMENT = 0x2; @@ -528,6 +532,8 @@ dictionary GPUTextureBindingLayout { enum GPUStorageTextureAccess { "write-only", + "read-only", + "read-write", }; dictionary GPUStorageTextureBindingLayout { @@ -536,7 +542,7 @@ dictionary GPUStorageTextureBindingLayout { GPUTextureViewDimension viewDimension = "2d"; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUBindGroup { }; GPUBindGroup includes GPUObjectBase; @@ -560,7 +566,7 @@ dictionary GPUBufferBinding { GPUSize64 size; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUPipelineLayout { }; GPUPipelineLayout includes GPUObjectBase; @@ -570,7 +576,7 @@ dictionary GPUPipelineLayoutDescriptor required sequence bindGroupLayouts; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUShaderModule { }; GPUShaderModule includes GPUObjectBase; @@ -586,7 +592,7 @@ enum GPUCompilationMessageType { "info", }; -[Exposed=(Window, DedicatedWorker), Serializable, SecureContext] +[Exposed=(Window, Worker), Serializable, SecureContext] interface GPUCompilationMessage { readonly attribute DOMString message; readonly attribute GPUCompilationMessageType type; @@ -596,11 +602,26 @@ interface GPUCompilationMessage { readonly attribute unsigned long long length; }; -[Exposed=(Window, DedicatedWorker), Serializable, SecureContext] +[Exposed=(Window, Worker), Serializable, SecureContext] interface GPUCompilationInfo { readonly attribute FrozenArray messages; }; +[Exposed=(Window, Worker), SecureContext, Serializable] +interface GPUPipelineError : DOMException { + constructor(optional DOMString message = "", GPUPipelineErrorInit options); + readonly attribute GPUPipelineErrorReason reason; +}; + +dictionary GPUPipelineErrorInit { + required GPUPipelineErrorReason reason; +}; + +enum GPUPipelineErrorReason { + "validation", + "internal", +}; + enum GPUAutoLayoutMode { "auto", }; @@ -616,13 +637,13 @@ interface mixin GPUPipelineBase { dictionary GPUProgrammableStage { required GPUShaderModule module; - required USVString entryPoint; + USVString entryPoint; record constants; }; -typedef double GPUPipelineConstantValue; // May represent WGSL’s bool, f32, i32, u32, and f16 if enabled. +typedef double GPUPipelineConstantValue; // May represent WGSL's bool, f32, i32, u32, and f16 if enabled. -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUComputePipeline { }; GPUComputePipeline includes GPUObjectBase; @@ -633,7 +654,7 @@ dictionary GPUComputePipelineDescriptor required GPUProgrammableStage compute; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPURenderPipeline { }; GPURenderPipeline includes GPUObjectBase; @@ -701,7 +722,7 @@ dictionary GPUBlendState { }; typedef [EnforceRange] unsigned long GPUColorWriteFlags; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] namespace GPUColorWrite { const GPUFlagsConstant RED = 0x1; const GPUFlagsConstant GREEN = 0x2; @@ -854,7 +875,7 @@ dictionary GPUImageCopyTexture { GPUTextureAspect aspect = "all"; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUCommandBuffer { }; GPUCommandBuffer includes GPUObjectBase; @@ -866,7 +887,7 @@ dictionary GPUCommandBufferDescriptor interface mixin GPUCommandsMixin { }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUCommandEncoder { GPURenderPassEncoder beginRenderPass(GPURenderPassDescriptor descriptor); GPUComputePassEncoder beginComputePass(optional GPUComputePassDescriptor descriptor = {}); @@ -933,7 +954,7 @@ interface mixin GPUDebugCommandsMixin { undefined insertDebugMarker(USVString markerLabel); }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUComputePassEncoder { undefined setPipeline(GPUComputePipeline pipeline); undefined dispatchWorkgroups(GPUSize32 workgroupCountX, optional GPUSize32 workgroupCountY = 1, optional GPUSize32 workgroupCountZ = 1); @@ -957,7 +978,7 @@ dictionary GPUComputePassDescriptor GPUComputePassTimestampWrites timestampWrites; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPURenderPassEncoder { undefined setViewport(float x, float y, float width, float height, @@ -1052,7 +1073,7 @@ interface mixin GPURenderCommandsMixin { undefined drawIndexedIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset); }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPURenderBundle { }; GPURenderBundle includes GPUObjectBase; @@ -1061,7 +1082,7 @@ dictionary GPURenderBundleDescriptor : GPUObjectDescriptorBase { }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPURenderBundleEncoder { GPURenderBundle finish(optional GPURenderBundleDescriptor descriptor = {}); }; @@ -1077,7 +1098,7 @@ dictionary GPURenderBundleEncoderDescriptor boolean stencilReadOnly = false; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUQueue { undefined submit(sequence commandBuffers); @@ -1098,7 +1119,7 @@ interface GPUQueue { }; GPUQueue includes GPUObjectBase; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUQuerySet { undefined destroy(); @@ -1118,7 +1139,7 @@ enum GPUQueryType { "timestamp", }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUCanvasContext { readonly attribute (HTMLCanvasElement or OffscreenCanvas) canvas; @@ -1146,7 +1167,7 @@ enum GPUDeviceLostReason { "destroyed", }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUDeviceLostInfo { readonly attribute GPUDeviceLostReason reason; readonly attribute DOMString message; @@ -1156,27 +1177,33 @@ partial interface GPUDevice { readonly attribute Promise lost; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUError { readonly attribute DOMString message; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUValidationError : GPUError { constructor(DOMString message); }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUOutOfMemoryError : GPUError { constructor(DOMString message); }; +[Exposed=(Window, Worker), SecureContext] +interface GPUInternalError + : GPUError { + constructor(DOMString message); +}; + enum GPUErrorFilter { "validation", "out-of-memory", - "internal" + "internal", }; partial interface GPUDevice { @@ -1184,8 +1211,21 @@ partial interface GPUDevice { Promise popErrorScope(); }; +[Exposed=(Window, Worker), SecureContext] +interface GPUUncapturedErrorEvent : Event { + constructor( + DOMString type, + GPUUncapturedErrorEventInit gpuUncapturedErrorEventInitDict + ); + [SameObject] readonly attribute GPUError error; +}; + +dictionary GPUUncapturedErrorEventInit : EventInit { + required GPUError error; +}; + partial interface GPUDevice { - [Exposed=(Window, DedicatedWorker)] + [Exposed=(Window, Worker)] attribute EventHandler onuncapturederror; };