diff options
author | Leo Kettmeir <crowlkats@toaxl.com> | 2024-05-05 07:22:18 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-05 07:22:18 -0700 |
commit | cd12d416271e9e9fd0f00303525021f75688ff9f (patch) | |
tree | af1fd7a88590e37b09e61ae5a1aaeec4c325b907 /ext/webgpu/01_webgpu.js | |
parent | b2628e4a069ed9fc13f34f6e4fd75f29c657e5a9 (diff) |
chore: update wgpu (#23684)
Diffstat (limited to 'ext/webgpu/01_webgpu.js')
-rw-r--r-- | ext/webgpu/01_webgpu.js | 480 |
1 files changed, 342 insertions, 138 deletions
diff --git a/ext/webgpu/01_webgpu.js b/ext/webgpu/01_webgpu.js index d3e9bf4e3..5ca8c0fdb 100644 --- a/ext/webgpu/01_webgpu.js +++ b/ext/webgpu/01_webgpu.js @@ -92,8 +92,7 @@ const { ArrayBuffer, ArrayBufferPrototypeGetByteLength, ArrayIsArray, - ArrayPrototypeFilter, - ArrayPrototypeIncludes, + ArrayPrototypeFindLast, ArrayPrototypeMap, ArrayPrototypePop, ArrayPrototypePush, @@ -104,12 +103,9 @@ const { ObjectHasOwn, ObjectPrototypeIsPrototypeOf, Promise, - PromisePrototypeCatch, - PromisePrototypeThen, PromiseReject, PromiseResolve, SafeArrayIterator, - SafePromiseAll, SafeSet, SafeWeakRef, SetPrototypeHas, @@ -124,7 +120,12 @@ const { } = primordials; import * as webidl from "ext:deno_webidl/00_webidl.js"; -import { EventTarget } from "ext:deno_web/02_event.js"; +import { + defineEventHandler, + Event, + EventTarget, + setEventTargetData, +} 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"; @@ -299,7 +300,6 @@ class GPUValidationError extends GPUError { this[_message] = message; } } -const GPUValidationErrorPrototype = GPUValidationError.prototype; class GPUOutOfMemoryError extends GPUError { name = "GPUOutOfMemoryError"; @@ -312,7 +312,40 @@ class GPUOutOfMemoryError extends GPUError { this[_message] = message; } } -const GPUOutOfMemoryErrorPrototype = GPUOutOfMemoryError.prototype; + +class GPUInternalError extends GPUError { + name = "GPUInternalError"; + constructor() { + super(illegalConstructorKey); + this[webidl.brand] = webidl.brand; + } +} + +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 + .GPUUncapturedErrorEventInit( + gpuUncapturedErrorEventInitDict, + prefix, + "Argument 2", + ); + + this.#error = gpuUncapturedErrorEventInitDict.error; + } + + get error() { + webidl.assertBranded(this, GPUUncapturedErrorEventPrototype); + return this.#error; + } +} +const GPUUncapturedErrorEventPrototype = GPUUncapturedErrorEvent.prototype; class GPU { [webidl.brand] = webidl.brand; @@ -433,7 +466,7 @@ class GPUAdapter { } } - const { rid, features, limits } = op_webgpu_request_device( + const { rid, queueRid, features, limits } = op_webgpu_request_device( this[_adapter].rid, descriptor.label, requiredFeatures, @@ -446,25 +479,22 @@ class GPUAdapter { features: createGPUSupportedFeatures(features), limits: createGPUSupportedLimits(limits), }); - return createGPUDevice( + const queue = createGPUQueue(descriptor.label, inner, queueRid); + inner.trackResource(queue); + const device = createGPUDevice( descriptor.label, inner, - createGPUQueue(descriptor.label, inner), + queue, ); + inner.device = device; + return device; } /** - * @param {string[]} unmaskHints * @returns {Promise<GPUAdapterInfo>} */ - requestAdapterInfo(unmaskHints = []) { + requestAdapterInfo() { webidl.assertBranded(this, GPUAdapterPrototype); - const prefix = "Failed to execute 'requestAdapterInfo' on 'GPUAdapter'"; - unmaskHints = webidl.converters["sequence<DOMString>"]( - unmaskHints, - prefix, - "Argument 1", - ); const { vendor, @@ -474,16 +504,10 @@ class GPUAdapter { } = op_webgpu_request_adapter_info(this[_adapter].rid); const adapterInfo = webidl.createBranded(GPUAdapterInfo); - adapterInfo[_vendor] = ArrayPrototypeIncludes(unmaskHints, "vendor") - ? vendor - : ""; - adapterInfo[_architecture] = - ArrayPrototypeIncludes(unmaskHints, "architecture") ? architecture : ""; - adapterInfo[_device] = ArrayPrototypeIncludes(unmaskHints, "device") - ? device - : ""; - adapterInfo[_description] = - ArrayPrototypeIncludes(unmaskHints, "description") ? description : ""; + adapterInfo[_vendor] = vendor; + adapterInfo[_architecture] = architecture; + adapterInfo[_device] = device; + adapterInfo[_description] = description; return PromiseResolve(adapterInfo); } @@ -687,6 +711,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; @@ -743,6 +775,8 @@ class GPUSupportedLimits { "maxVertexAttributes", "maxVertexBufferArrayStride", "maxInterStageShaderComponents", + "maxColorAttachments", + "maxColorAttachmentBytesPerSample", "maxComputeWorkgroupStorageSize", "maxComputeInvocationsPerWorkgroup", "maxComputeWorkgroupSizeX", @@ -773,6 +807,7 @@ class GPUSupportedFeatures { constructor() { webidl.illegalConstructor(); } + [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) { if (ObjectPrototypeIsPrototypeOf(GPUSupportedFeaturesPrototype, this)) { return `${this.constructor.name} ${ @@ -783,7 +818,6 @@ class GPUSupportedFeatures { } } } - const GPUSupportedFeaturesPrototype = GPUSupportedFeatures.prototype; /** @@ -870,7 +904,7 @@ function GPUObjectBaseMixin(name, type) { /** * @typedef ErrorScope * @property {string} filter - * @property {Promise<void>[]} operations + * @property {GPUError[]} errors */ /** @@ -879,6 +913,7 @@ function GPUObjectBaseMixin(name, type) { * @property {number | undefined} rid * @property {GPUSupportedFeatures} features * @property {GPUSupportedLimits} limits + * @property {GPUDevice} device */ class InnerGPUDevice { @@ -900,6 +935,8 @@ class InnerGPUDevice { resolveLost; /** @type {ErrorScope[]} */ errorScopeStack; + /** @type {GPUDevice} */ + device; /** * @param {InnerGPUDeviceOptions} options @@ -923,79 +960,51 @@ class InnerGPUDevice { ArrayPrototypePush(this.resources, new SafeWeakRef(resource)); } - /** @param {{ type: string, value: string | null } | undefined} err */ - pushError(err) { - this.pushErrorPromise(PromiseResolve(err)); - } - - /** @param {Promise<{ type: string, value: string | null } | undefined>} promise */ - pushErrorPromise(promise) { - const operation = PromisePrototypeThen(promise, (err) => { - if (err) { - switch (err.type) { - case "lost": - this.isLost = true; - this.resolveLost( - createGPUDeviceLostInfo(undefined, "device was lost"), - ); - break; - case "validation": - return PromiseReject( - new GPUValidationError(err.value ?? "validation error"), - ); - case "out-of-memory": - return PromiseReject(new GPUOutOfMemoryError()); - } - } - }); + // Ref: https://gpuweb.github.io/gpuweb/#abstract-opdef-dispatch-error + /** @param {{ type: string, value: string | null } | undefined} error */ + pushError(error) { + if (!error) { + return; + } - const validationStack = ArrayPrototypeFilter( - this.errorScopeStack, - ({ filter }) => filter == "validation", - ); - const validationScope = validationStack[validationStack.length - 1]; - const validationFilteredPromise = PromisePrototypeCatch( - operation, - (err) => { - if (ObjectPrototypeIsPrototypeOf(GPUValidationErrorPrototype, err)) { - return PromiseReject(err); - } - return PromiseResolve(); - }, - ); - if (validationScope) { - ArrayPrototypePush( - validationScope.operations, - validationFilteredPromise, - ); - } else { - PromisePrototypeCatch(validationFilteredPromise, () => { - // TODO(lucacasonato): emit an UncapturedErrorEvent - }); + let constructedError; + switch (error.type) { + case "lost": + this.isLost = true; + this.resolveLost( + createGPUDeviceLostInfo(undefined, "device was lost"), + ); + return; + case "validation": + constructedError = new GPUValidationError( + error.value ?? "validation error", + ); + break; + case "out-of-memory": + constructedError = new GPUOutOfMemoryError(); + break; + case "internal": + constructedError = new GPUInternalError(); + break; } - // prevent uncaptured promise rejections - PromisePrototypeCatch(validationFilteredPromise, (_err) => {}); - const oomStack = ArrayPrototypeFilter( + if (this.isLost) { + return; + } + + const scope = ArrayPrototypeFindLast( this.errorScopeStack, - ({ filter }) => filter == "out-of-memory", + ({ filter }) => filter === error.type, ); - const oomScope = oomStack[oomStack.length - 1]; - const oomFilteredPromise = PromisePrototypeCatch(operation, (err) => { - if (ObjectPrototypeIsPrototypeOf(GPUOutOfMemoryErrorPrototype, err)) { - return PromiseReject(err); - } - return PromiseResolve(); - }); - if (oomScope) { - ArrayPrototypePush(oomScope.operations, oomFilteredPromise); + if (scope) { + ArrayPrototypePush(scope.errors, constructedError); } else { - PromisePrototypeCatch(oomFilteredPromise, () => { - // TODO(lucacasonato): emit an UncapturedErrorEvent - }); + this.device.dispatchEvent( + new GPUUncapturedErrorEvent("uncapturederror", { + error: constructedError, + }), + ); } - // prevent uncaptured promise rejections - PromisePrototypeCatch(oomFilteredPromise, (_err) => {}); } } @@ -1011,6 +1020,7 @@ function createGPUDevice(label, inner, queue) { device[_label] = label; device[_device] = inner; device[_queue] = queue; + setEventTargetData(device); return device; } @@ -1283,11 +1293,6 @@ class GPUDevice extends EventTarget { const resource = entry.resource; if (ObjectPrototypeIsPrototypeOf(GPUSamplerPrototype, resource)) { const rid = assertResource(resource, prefix, context); - assertDeviceMatch(device, resource, { - prefix, - resourceContext: context, - selfContext: "this", - }); return { binding: entry.binding, kind: "GPUSampler", @@ -1298,11 +1303,6 @@ class GPUDevice extends EventTarget { ) { const rid = assertResource(resource, prefix, context); assertResource(resource[_texture], prefix, context); - assertDeviceMatch(device, resource[_texture], { - prefix, - resourceContext: context, - selfContext: "this", - }); return { binding: entry.binding, kind: "GPUTextureView", @@ -1311,12 +1311,6 @@ class GPUDevice extends EventTarget { } 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, - selfContext: "this", - }); return { binding: entry.binding, kind: "GPUBufferBinding", @@ -1509,12 +1503,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; } /** @@ -1626,7 +1774,7 @@ class GPUDevice extends EventTarget { webidl.requiredArguments(arguments.length, 1, prefix); filter = webidl.converters.GPUErrorFilter(filter, prefix, "Argument 1"); const device = assertDevice(this, prefix, "this"); - ArrayPrototypePush(device.errorScopeStack, { filter, operations: [] }); + ArrayPrototypePush(device.errorScopeStack, { filter, errors: [] }); } /** @@ -1647,12 +1795,7 @@ class GPUDevice extends EventTarget { "OperationError", ); } - const operations = SafePromiseAll(scope.operations); - return PromisePrototypeThen( - operations, - () => PromiseResolve(null), - (err) => PromiseResolve(err), - ); + return PromiseResolve(scope.errors[0] ?? null); } [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) { @@ -1676,23 +1819,60 @@ class GPUDevice extends EventTarget { } GPUObjectBaseMixin("GPUDevice", GPUDevice); const GPUDevicePrototype = GPUDevice.prototype; +defineEventHandler(GPUDevice.prototype, "uncapturederror"); + +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]; + + [_cleanup]() { + const rid = this[_rid]; + if (rid !== undefined) { + core.close(rid); + /** @type {number | undefined} */ + this[_rid] = undefined; + } + } constructor() { webidl.illegalConstructor(); @@ -1726,7 +1906,7 @@ class GPUQueue { return rid; }, ); - const { err } = 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; } @@ -1782,7 +1962,7 @@ class GPUQueue { } const { err } = op_webgpu_write_buffer( - device.rid, + this[_rid], bufferRid, bufferOffset, dataOffset, @@ -1833,7 +2013,7 @@ class GPUQueue { } const { err } = op_webgpu_write_texture( - device.rid, + this[_rid], { texture: textureRid, mipLevel: destination.mipLevel, @@ -2027,19 +2207,15 @@ class GPUBuffer { this[_mapMode] = mode; this[_state] = "pending"; - const promise = PromisePrototypeThen( - op_webgpu_buffer_get_map_async( - bufferRid, - device.rid, - mode, - offset, - rangeSize, - ), - ({ err }) => err, + const { err } = await op_webgpu_buffer_get_map_async( + bufferRid, + device.rid, + mode, + offset, + rangeSize, ); - device.pushErrorPromise(promise); - const err = await promise; if (err) { + device.pushError(err); throw new DOMException("validation error occurred", "OperationError"); } this[_state] = "mapped"; @@ -5136,6 +5312,7 @@ webidl.converters["GPUFeatureName"] = webidl.createEnumConverter( "texture-compression-astc", "rg11b10ufloat-renderable", "bgra8unorm-storage", + "float32-filterable", // extended from spec @@ -5174,6 +5351,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 }); @@ -5353,6 +5551,7 @@ webidl.converters["GPUTextureFormat"] = webidl.createEnumConverter( "bgra8unorm", "bgra8unorm-srgb", "rgb9e5ufloat", + "rgb10a2uint", "rgb10a2unorm", "rg11b10ufloat", "rg32uint", @@ -5768,6 +5967,8 @@ webidl.converters["GPUStorageTextureAccess"] = webidl.createEnumConverter( "GPUStorageTextureAccess", [ "write-only", + "read-only", + "read-write", ], ); @@ -6027,7 +6228,6 @@ const dictMembersGPUProgrammableStage = [ { key: "entryPoint", converter: webidl.converters["USVString"], - required: true, }, { key: "constants", @@ -6110,6 +6310,7 @@ webidl.converters["GPUVertexFormat"] = webidl.createEnumConverter( "sint32x2", "sint32x3", "sint32x4", + "unorm10-10-10-2", ], ); @@ -7070,6 +7271,7 @@ webidl.converters["GPUErrorFilter"] = webidl.createEnumConverter( [ "out-of-memory", "validation", + "internal", ], ); @@ -7209,6 +7411,7 @@ export { GPUDevice, GPUDeviceLostInfo, GPUError, + GPUInternalError, GPUMapMode, GPUOutOfMemoryError, GPUPipelineLayout, @@ -7226,5 +7429,6 @@ export { GPUTexture, GPUTextureUsage, GPUTextureView, + GPUUncapturedErrorEvent, GPUValidationError, }; |